vuejs / core

🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
https://vuejs.org/
MIT License
47.75k stars 8.35k forks source link

Memory leak #12370

Closed huodoushigemi closed 1 week ago

huodoushigemi commented 1 week ago

Vue version

3.5.12

Link to minimal reproduction

https://play.vuejs.org/#eNp9UkFuwjAQ/MrKlzpSFEDtCQESRUhtD21Vqp58icIGQh3bsh2gQvl71w6hHCpySOKZ2fHs2ic2NybbN8jGbOIKWxkPDn1jZkJVtdHWwwkslikccl9sU8CyxMKvCm0QWiitruGOyu+EEqqQuXPwtYifE7QR08qRZdRPr6t5EuhIZLZRnCcwncFJKICuRlb0mobN+dza/IePhsNhkpWVlDzJ6tzwc43Cw3lTntATbHsP57Uhj5idB8MUzvtAS0KAwSBqQpgA9IF6bDLoZkLToIXH2sjcI60AJtvR7Aml1HDQVq4nA1qHgouIpcyTmyqrTbZzWtGEY3eCFbo2lUT7ZnxFMQUbd30HLifHw0vEvG0w7fFii8X3P/jOHQMm2LtFh3aPgl04n9sN+o5erl7xSP8XstbrRpL6BvmBTssmZOxkj41aU+wrXUz7HO9JpTafbnn0qFzfVAgalG3UC0b3ZHGj9b+499lDrKNDYe0vcNPd8w==

Steps to reproduce

Start chrome Open the linked SFC Playground Open devtools > Memory Invoke garbage collection (click on trash) Create snapshot In the created snapshot, filter for VClass objects

What is expected?

list should be garbage collected

What is actually happening?

list should be garbage collected

System Info

No response

Any additional comments?

No response

skirtles-code commented 1 week ago

I'm not sure this is a memory leak. If I set the Playground to PROD mode, the VClass instances are no longer retained in memory.

I believe the reason they're being retained is due to the proxying used to expose variables to the template in dev. In production mode, the variable scope is eligible for GC, so list and the VClass instances are no longer present.

But perhaps using production mode is 'cheating', in the sense that I'm bypassing what the reproduction was trying to show.

Perhaps the intention was just to illustrate that stopping an EffectScope doesn't free the effects it contains?

But I'm not sure whether that is necessarily a problem either. If I might use an analogy, when a component instance is unmounted we do a certain amount of teardown, but we don't teardown everything. A lot of the internals are left intact and it's down to the garbage collector to free up that memory. If someone retains an external reference to the component instance then those internals will still show in a memory dump, but it isn't really a leak.

I think what's happening here is essentially the same thing. The EffectScope is only doing a limited amount of internal teardown, then relying on GC to do the rest. If you retain a reference to the EffectScope instance then you'll get some of the internals with it.

It isn't immediately clear to me whether EffectScope should be doing more internal teardown, or whether it's just not valid to retain a reference to it after it's been stopped.

yyx990803 commented 1 week ago

Like @skirtles-code pointed out, I don't think this technically qualifies as a leak. But considering standalone usage of @vue/reactivity, it's still nice to release nested resources on stop in case the scope reference is retained for some reason.