Skip to content
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

A memory leak occurred when switching between components #6591

Closed
Mario34 opened this issue Sep 3, 2022 · 15 comments
Closed

A memory leak occurred when switching between components #6591

Mario34 opened this issue Sep 3, 2022 · 15 comments
Labels
🐞 bug Something isn't working

Comments

@Mario34
Copy link

Mario34 commented Sep 3, 2022

Version

4.1.5

Reproduction link

github.com (has router)
SFC Playground

Steps to reproduce

  • toggle between pages multiple times
  • check memory usage after GC

What is expected?

Stable memory usage

What is actually happening?

A large number of nodes are not reclaimed, and memory keeps growing

@Mario34
Copy link
Author

Mario34 commented Sep 3, 2022

image

Snapshot 3 is generated after switching from '5000 items' to' Empty '. You can see that the number of separated nodes is basically the same as that on the page

@MMMMiller
Copy link

same condition found

@posva posva transferred this issue from vuejs/router Sep 3, 2022
@posva posva changed the title A memory leak occurred when switching between pages A memory leak occurred when switching between components Sep 3, 2022
@posva
Copy link
Member

posva commented Sep 3, 2022

Transfered from Vue router and added a repro without it

@posva posva added 🐞 bug Something isn't working regression labels Sep 3, 2022
@moushicheng
Copy link
Contributor

This does not seem to be < component /> problem
using v-if,there will still be memory leaks,
see here:
SFC Playground

@michaelperel
Copy link

I am experiencing the same issue in multiple projects I am working on using vue 3 with the router

@michaelperel
Copy link

Here is a link to @moushicheng 's example without the EmptyPage component. This demonstrates that there is a memory leak in extremely simple cases when using v-if. I tried with different Vue 3 versions and the results are all the same.

If you replace v-if with v-show, there is no memory leak.

@posva posva added the 🔥 p5-urgent Priority 5: this fixes build-breaking bugs that affect most users and should be released ASAP. label Sep 10, 2022
@yyx990803 yyx990803 removed 🔥 p5-urgent Priority 5: this fixes build-breaking bugs that affect most users and should be released ASAP. regression labels Sep 27, 2022
@yyx990803
Copy link
Member

yyx990803 commented Sep 27, 2022

Update: the fix only addresses the simplified cases here and here.


This is fixed in fc5bdb3

Some notes:

  1. This is not technically a "memory leak". The root cause is that hoisted vnodes are retaining access to detached DOM nodes, and the detached DOM nodes in turn retain its parent / sibling trees. There is only one copy of the trees retained at any time, so the total memory does not increase over time even in the pre-fix behavior - thus not really a "leak", but does result in unnecessary memory cost.

  2. This is not a regression and has been present in all versions of Vue 3.

@yyx990803 yyx990803 reopened this Sep 28, 2022
@yyx990803
Copy link
Member

Looks like the simplified reproduction is a bit different from the original case, where there is a legit memory leak that is still present in 3.2.40.

@yyx990803
Copy link
Member

More investigation: the leak in the original repro is actually caused by the devtools. There is no leak in production.

@yyx990803
Copy link
Member

yyx990803 commented Sep 28, 2022

The "leak" only happens when:

  • Using the dev build
  • Has Vue Devtools enabled
  • Hasn't opened the Vue devtools tab

Vue Devtools buffers devtool-specific events in a global object before the devtools tab is activated. The buffered events hold references to the component instances triggering them, so those component instances, along with their DOM, are never released from memory until the devtools tab is activated and the buffer is cleared.

Although this does not affect production cases, it could cause perf issues when toggling between heavy components during development. It could also be confusing to users when debugging memory issues.

@Akryum this behavior seems to have been in devtools for a long time, I'm not sure if the memory issue is the result of a recent change or has always been there as well. Is there anyway we can rework this in the devtools to avoid it?

Minimal code needed to reproduce (using local global build):

<script src="https://unpkg.com/vue"></script>

<div id="app"></div>

<script>
  const { ref, h } = Vue

  const Foo = {
    template: `
    <div class="page">
      <span v-for="i in 10">{{ i }}</span>
    </div>`
  }

  const app = Vue.createApp({
    setup() {
      const ok = ref(false)
      return () => {
        return [
          h('button', { onClick: () => (ok.value = !ok.value) }, 'toggle'),
          ok.value ? h(Foo) : null
        ]
      }
    }
  })

  app.mount('#app')
</script>

@michaelperel
Copy link

michaelperel commented Sep 28, 2022

@yyx990803 I believe the memory leak still exists, in prod, in the original case, without vue dev tools.

On a fresh install of Chrome, in private browsing, using the SFC link, here is a screenshot of the memory:
memory_bug

  1. The first snapshot is when the page loads, and the button has not been pressed (no evidence of memory leak).
  2. The second snapshot is when the button is pressed again and all the items are showing (no evidence of memory leak).
  3. The third snapshot is when the button is pressed again, and none of the items is showing, and garbage has been collected. Notice that the memory is the same as when all of the nodes are showing, but should be the same as in step 1. This is evidence of a memory leak.
  4. The fourth snapshot is when the button has been pressed again, twice, so the nodes are all showing again. The memory is almost twice what it should be. This is evidence of a memory leak.

The memory will not grow beyond step 4, no matter how much you press the button. So it doesn't grow indefinitely, but it does retain memory that should be freed. This is a memory leak (not unbounded).

This is a huge problem when you have webpages with many big components - in this case, the memory retention resembles an unbounded memory leak because if each component retains its original size when it goes away, and double its size when it is shown for the second time, memory use will grow tremendously.

For instance, imagine you have an application where you can toggle between tables as a dashboard, where only 1 table is shown at a time. Each table uses 100mb. When no table is being shown, the application uses ~2mb of memory. When you show a table it uses 100mb. When you toggle the table and it goes away, it still uses 100mb when it should only use ~2mb. If you toggle the table multiple times, it uses up to 200mb at most. If you do this with multiple tables that are all different components, and imagine there are 20 tables/components, you will run out of memory and crash the tab, when at most the application should use 100mb while displaying a table.

@Akryum
Copy link
Member

Akryum commented Sep 28, 2022

@michaelperel The devtools are enabled on the SFC playground even in production mode.

@Akryum
Copy link
Member

Akryum commented Sep 28, 2022

@michaelperel What your are describing is exactly what @yyx990803 said in the previous comment:
image

@michaelperel
Copy link

Thank you so much for clearing that up! I figured because it said “prod” that it meant they were disabled.

Thank you all so much for your work on this issue, I really really appreciate it :)

@binvb
Copy link

binvb commented Nov 8, 2022

thank you , help me a lot

chrislone pushed a commit to chrislone/core that referenced this issue Feb 4, 2023
chrislone pushed a commit to chrislone/core that referenced this issue Feb 4, 2023
@github-actions github-actions bot locked and limited conversation to collaborators Sep 19, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
🐞 bug Something isn't working
Projects
None yet
Development

No branches or pull requests

8 participants