Subscribe on changes!

Detached nodes after unmount component(Memory leaks)

avatar
Feb 5th 2022

Version

3.2.29

Reproduction link

github.com

Steps to reproduce

first step run npm run build, open dist/index.html vue3-1 highlight element with id app , write in console $0.__vue_app__.unmount() after unmount app create heap snapshot, we have memory leaks in heap snapshot vue3-2

What is expected?

zero detached dom elements and detached vue EventListeners...

What is actually happening?

exist in heap snapshot: 2001 Detached HTMLDivElement Detached EventListener ...

avatar
Feb 8th 2022

It's not memory leak. This is due to vue-devtools recorded some information which contains thoese elements and this only happen on development environment(I can't reproduced what you said), but I think vue-devtools should be improved.

avatar
Feb 8th 2022

@Akryum Do you agree with @caozhong1996? Should we move this issue to the devtools repo?

avatar
Feb 8th 2022

@caozhong1996 memory leak reproducible, I showed an example without vue-devtools(prod build npm run build) see url in second screenshot ends with dist/index.html , and i reproduce in incognito mode, please check again

avatar
Feb 8th 2022

Sorry, I misunderstood you, it's similar to vue-devtools's behavior, but it's another problem😥. There is a related issue

It seems that those invokers not be cleaned during unmount.

https://github.com/vuejs/core/blob/c35ec47d73212b1b1fb1abca9004f992c45aa942/packages/runtime-dom/src/modules/events.ts#L72

avatar
Feb 8th 2022

@caozhong1996 Thanks for the research, and what to do about it? Also i made an issue with the same problem on vue 2 please answer it too(bug also reproduce after prod build npm run build for vue 2 have detached nodes, check pls) link for the same issue memory leak for vue 2 - > https://github.com/vuejs/vue/issues/12445

avatar
Feb 8th 2022

@caozhong1996 Interesting. I don't see an apparent reason though why these should not be garbage collectable - the elements are detached, the reference to the component instance in the invoker's closure should also not be a reason as those instances have been unmounted and detached as well.

Do you have an idea/suspicion?

avatar
Feb 9th 2022

I'm just as confused by this as you, even in this situation which still appeared😥

<!-- no event binding -->
<div v-for="item in 1000" v-bind:key="item">
  <div>111<div/>
</div>
avatar
Feb 9th 2022

@caozhong1996 what exactly confused you ?

avatar
Feb 9th 2022

We just have a hard time figuring out a reason as to why detached elements aren't garbage collected.

avatar
Feb 9th 2022

@LinusBorg In view 2 the same behavior of detached nodes after destoy App. https://github.com/vuejs/vue/issues/12445, maybe this isue for vue 2 will be useful

avatar
Feb 9th 2022

I'm just as confused by this as you, even in this situation which still appeared😥

<!-- no event binding -->
<div v-for="item in 1000" v-bind:key="item">
  <div>111<div/>
</div>

@caozhong1996 Event binding is in the example, on the button element

avatar
Feb 11th 2022

@LinusBorg Now I'm pretty sure it's a bug.

We created invoker at here and reference component instance.

https://github.com/vuejs/core/blob/c35ec47d73212b1b1fb1abca9004f992c45aa942/packages/runtime-dom/src/modules/events.ts#L107-L132

These elements reference invoker's closure (_vei) https://github.com/vuejs/core/blob/c35ec47d73212b1b1fb1abca9004f992c45aa942/packages/runtime-dom/src/modules/events.ts#L81-L82

So after removeChild, these elements become detached, even can't remove component instance.

https://github.com/vuejs/core/blob/9aa5dfd4bb8efac0041e33ef5fdbebab59cc6516/packages/runtime-dom/src/nodeOps.ts#L17

Because we should remove these references before removeChild:

As long as a reference is kept on the removed child, it still exists in memory, but is no longer part of the DOM. It can still be reused later in the code. mdn

avatar
Feb 11th 2022

the invoker closure doesn't reference the element, it only references the instance. the MDN statement you quoted refers to things that are still active on the main thread (Whatever the correct term may be) having a reference on the removed element, which is not the case here?

avatar
Feb 11th 2022

@LinusBorg here we go 无标题-2022-02-11-1656 impicture_20220211_175745 impicture_20220211_180029

avatar
May 2nd 2022

Any update on this? This memory leak is hurting our application's performance.

avatar
May 10th 2022

I investigated the repro and noticed a few very complicated conditions. For the original reproduction, the "leak" only happens if I click the button first, then unmount the app via the inspector + console.

  • If I do not click the button, then the leak doesn't happen. This is a weird condition.
  • If the app is unmounted programmatically in other ways, the leak also disappears:

If you change main.js to the following:

const app = createApp(App)
app.mount('#app')

setTimeout(() => {
  app.unmount()
}, 1000)

The leak doesn't happen after app is unmounted.

If you add a button outside of the app (in public/index.html), and then manually attach an event listener to it that calls app.unmount(), the leak also doesn't happen. So I don't think the leak is caused by Vue's patching mechanism, and in some way Chrome devtool inspection also affects the behavior.

The leak does happen if the unmount is triggered by an event dispatched from an element within the unmounted parent element. Interestingly, this seems to be a Chrome bug - because I was able to reproduce this without Vue involved at all in this gist.

In addition, this leak seems to be "temporary" - i.e. if this process is repeated by clicking the "reset" button in the above gist, the total amount of nodes in memory does not increase. In fact, I've noticed that the detached nodes are garbage collected when another event listener is triggered. Previously detached nodes are somehow reclaimed. So in some way, this isn't really a "leak" since the total memory does not increase over time.


To sum up: this seems to be a Chrome issue, and I couldn't find any way to "fix" this in Vue, since even replacing current event patching with plain addEventListener calls makes no difference from my tests. The fact that it also affects Vue 2 may be an indication that this is a recently introduced bug in Chrome. I've filed a Chrome bug here: https://bugs.chromium.org/p/chromium/issues/detail?id=1324096

avatar
Aug 5th 2022

Try @JhonWeawer 's reproduce repo on chrome 104.0.5112.79 with vuejs@3.2.37, still reproduce this problem.

It seems not a chrome issue or the fixed issue still not work with vuejs.

@LinusBorg @yyx990803 Is this would be fixed? It hurts performance.

avatar
Sep 1st 2022

I am seeing the same issue of a large number of detached html elements. However, I have noticed this in chrome. Firefox works fine. If this is a chrome issue, can someone link it here?

avatar
Apr 14th 2023

I tested @yyx990803's gist in chrome 104 and 112, while the chromium issue appeared to have been fixed in 104, it's back in 112 or something similar anyways. I also tested the original reproduction with Vue 3.2.47 in both chrome 104 and 112 and I see detached nodes in both versions, so as mentioned above it doesn't seem like it was ever fixed.