Vue-apollo: Memory leak when using apollo

Created on 17 Sep 2020  路  5Comments  路  Source: vuejs/vue-apollo

Describe the bug
When using apollo like props in a component, after we remove this component, the number of DOM nodes does not decrease. Because of this, memory is constantly growing.

If you do not use Apollo in a component, then after its destruction, the DOM nodes disappear from memory using the garbage collector and memory usage drops.

cache.evict() and cache.gc() delete data from the cache, but does not help solve the problem.

To Reproduce
I made a repository with an example and recorded a short video on how to reproduce this problem.

Expected behavior
DOM nodes should be removed from memory when a component is removed.

Versions

"@apollo/client": "^3.1.4",
"graphql": "^15.3.0",
"graphql-tag": "^2.11.0",
"vue": "^2.6.11",
"vue-apollo": "^3.0.4"
bug

All 5 comments

Seeing this as well, the only way to help alleviate the issue has been to extend the normal Vue destroy hook and manually prep everything for garbage collection.

Seeing this as well, the only way to help alleviate the issue has been to extend the normal Vue destroy hook and manually prep everything for garbage collection.

Can you show an example for this, please?

@frederikhors We've been experimenting with some variation of this:

const blockedProps = [ 
  '$data',
  '$listeners',
  '$attrs',
  '$props'
]

Vue.mixin({
  destroyed() {
    const prepForGC = () => {
      try {
        this.$el?.remove()
        this.elm = null
        this.$el = null
        this.parent = null
        this.$parent = null
        this.data = null
        // this.$data = null // Unsetting this throws internal Vue errors
        // this.options = null // Unsetting this throws internal Vue errors
        this.$options = null
        this.$vnode = null
        this.listeners = null
        // this.$listeners = null // Unsetting this throws internal Vue errors
        this._vnode = null
        this._watcher = null
        this._watchers = null
        this._computedWatchers = null
        this.$slots = null
        this.slots = null
        this.$scopedSlots = null
        this.scopedSlots = null
        this.$children = null
        this.children = null
        this.store = null
        this.$store = null

        // $apollo has no setter so we clear props instead
        if (this.$apollo) {
          this.$apollo.vm = null
        }

        Object.keys(this).forEach(key => {
          try {
            !blockedProps.includes(key) &&
              !(key in this[key]?.$props) &&
              (this[key] = null)
          } catch {
            /* */
          }
        })
      } catch (e) {
        // eslint-disable-next-line no-console
        console.error('Error in destroyed garbage collector mixin', e)
      }
    }

    // If the element has already been removed from the DOM
    // we run the GC prep method immediately
    if (!document.body.contains(this.$el)) {
      prepForGC()
      return
    }

    // Otherwise we can assume there's some sort of transition
    // on the component and we wait for it to complete
    // Note: this is obviously rough but Observer methods
    // were performing poorly.
    setTimeout(prepForGC, 1500)
  }
})

I can't reproduce with this forked with updated dependencies and fixed build: https://github.com/Akryum/vue-apollo-memory-leak

Was this page helpful?
0 / 5 - 0 ratings

Related issues

igaloly picture igaloly  路  3Comments

Akryum picture Akryum  路  3Comments

agosto-chrisbartling picture agosto-chrisbartling  路  4Comments

wangxiangyao picture wangxiangyao  路  4Comments

AruXc picture AruXc  路  4Comments