Skip to content

Memory leak (detached DOM tree) when patching vnodes #5851

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
bartsidee opened this issue Jun 9, 2017 · 0 comments
Closed

Memory leak (detached DOM tree) when patching vnodes #5851

bartsidee opened this issue Jun 9, 2017 · 0 comments

Comments

@bartsidee
Copy link

bartsidee commented Jun 9, 2017

Version

2.3.4

Reproduction link

https://jsfiddle.net/bartsidee/n1tvtwet/

Steps to reproduce

We identified that when a vnod is patched/replaced in the DOM e.g. after a vue-router route update the new vnode is marked with a DOM element reference (_refElm) of the previous vnode (in our case the previous route) causing a detached dom tree (memory leak)

Steps to reproduce:

  1. open attached jsfiddle
  2. open chrome dev tools -> memory tab
  3. select the 'Home' route
  4. make a heap snapshot
  5. select the 'Foo' route
  6. make a heap snapshot
  7. select the last snapshot and use the 'summary' tab
  8. in the search box enter 'detached'
  9. find the detached tree that has a retainer to '_refElm'
  10. if you hover the red HTMLDivElement you will notice it is the dom element of the 'Home' component

See below code reference in vuejs:

  1. "patch" method is triggered which trigger "patchVnode" to replace existing root component
    patchVnode(oldVnode, vnode, insertedVnodeQueue, removeOnly)
  2. both root components have children, which triggers "updateChildren":
    if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)
  3. there are no transitions so a new element is created, triggering "createElm":
    createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm)
  4. important is that "createElm" here is passed a DOM element reference "oldStartVnode.elm" of the old vnode (previous route) as 4th argument
  5. this in turn is link that the old dom element to the new vnode as "_refElm" property creating the detached binding causing the leak
    function createElm (vnode, insertedVnodeQueue, parentElm, refElm, nested) {

    opts._refElm = options._refElm

What is expected?

we would expect that VueJS would be conservative of memory usage and prevent detached dom nodes

What is actually happening?

when patching the vdom and replacing the root node, e.g. using the vue-router we see that the dom element of the previous route is indirectly linked to the new route causing a detached dom tree.


The leak has a limited impact for normal devices as the reference is only to the last component that is patched, but can have a large impact on for example embedded devices as it prevents to browser to garbage collect to last route increasing the overall memory needed to run the app.

So to be clear if we have 3 components: component 1, component 2, component 3 and component 1 is patched with component 2, vue will keep a detached dom of component 1. If component 2 is patched with component 3, component 2 will also be detached, but component 1 is in this case cleared because only the 'elm' reference transitions from component 2 to component 3.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant