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

Memory leak on first route visit if component has single root with multiple children #730

Closed
MarMun opened this issue Jan 22, 2021 · 12 comments

Comments

@MarMun
Copy link

MarMun commented Jan 22, 2021

Version

3.0.5

Reproduction link

https://github.com/MarMun/vue-memleak

Steps to reproduce

  1. Serve production build npm run servep
  2. Open browser and navigate to locally served project (e.g. localhost:5000)
  3. Open dev tools
  4. Navigate to "Case 3" and back to "Home"
  5. Issue garbage collection manually (DOM nodes from "Case 3"-view stick)
  6. Navigate again to "Case 3" and back to "Home"
  7. Issue garbage collection manually (DOM nodes from "Case 3"-view visit 1(?) still stick, but DOM nodes from visit 2(?) are GC'ed)

What is expected?

DOM nodes from previous route visit (to "Case 3") can be (manually) garbage collected

What is actually happening?

DOM nodes from previous route visit (to "Case 3") can NOT be (manually) garbage collected


Assumption

It seems that DOM nodes are not collectable by GC after first route visit if component has single root with multiple child's.

No mem leak: No single root. Multiple children (Case 1)

<template>

    <h1>Case 1 (no mem leak)</h1>

    <FooBarList/>

</template>

No mem leak: Single root with one children (Case 2)

<template>

  <div>
    <FooBarList/>
  </div>

</template>

MEM LEAK: Single root with multiple children (Case 3)

<template>

    <div>
      <h1>Case 3 (mem leak)</h1>

      <FooBarList/>
    </div>

</template>
@MarMun
Copy link
Author

MarMun commented Jan 22, 2021

npm list --depth=0
vue-memleak@0.1.0 /Users/martin/Dev/vue-memleak
├── @vue/cli-plugin-eslint@4.5.10
├── @vue/cli-plugin-router@4.5.10
├── @vue/cli-service@4.5.10
├── @vue/compiler-sfc@3.0.5
├── @vue/eslint-config-standard@5.1.2
├── eslint@6.8.0
├── eslint-plugin-import@2.22.1
├── eslint-plugin-node@11.1.0
├── eslint-plugin-promise@4.2.1
├── eslint-plugin-standard@4.1.0
├── eslint-plugin-vue@7.5.0
├── serve@11.3.2
├── vue@3.0.5
└── vue-router@4.0.3

@LinusBorg LinusBorg transferred this issue from vuejs/core Jan 22, 2021
@posva
Copy link
Member

posva commented Jan 22, 2021

Navigating to home and then to case 3 many times and the count and memory usage is stable:

Screenshot 2021-01-22 at 16 15 20

Screenshot 2021-01-22 at 16 15 05

@posva posva closed this as completed Jan 22, 2021
@MarMun
Copy link
Author

MarMun commented Jan 22, 2021

@posva Thx for looking into this!

Count and memory appear to be stable but they aren't.

Each route you open the first time contributes to a new baseline (as leak happens only on first visit).

I have added a "case 4" route (which also leaks):

(numbers = case view opened / GC'ed)

Screenshot 2021-01-22 at 19 17 35

git pull origin master to reproduce

@posva
Copy link
Member

posva commented Jan 23, 2021

I tried again and the count is stable on my end, there is no memory leak.
It does increase depending on the views you add but it does not grow indefinitely (like a memory leak)

@MarMun
Copy link
Author

MarMun commented Jan 26, 2021

Thx for looking at it again @posva. Much appreciated.

it does not grow indefinitely

I would says thats because everything that could leak in the example project already leaked ;-)

Add e. g. just 5 more routes with 5k dom nodes each and there are an extra 25k Dom nodes hanging in memory forever without being used or GC’ed.

What puzzles me: Why do cases 1 & 2 behaviour differ compared to 3 & 4 ? Is there a special reason I don’t see?

@MarMun
Copy link
Author

MarMun commented Apr 20, 2021

Persists in vue-router 4.0.6 / vue 3.0.11

@MarMun
Copy link
Author

MarMun commented Jul 12, 2021

Persists in vue-router 4.0.10 / vue 3.1.4

@MarMun
Copy link
Author

MarMun commented Mar 8, 2022

Persists in vue-router 4.0.13 / vue 3.2.31

@michaelperel
Copy link

@MarMun I am experiencing this issue as well. Did you ever happen to understand more about it?

@MarMun
Copy link
Author

MarMun commented Sep 5, 2022

@michaelperel Thx for reaching out. I gave it another thought and came up with these questions:

Does it only leak if the root component of a route has 'problematic' layout or do a nested child with 'problematic' layout also cause the leak?

To answer that, I created another case: I wrapped case 4 inside the layout of case 2 (also tested case 1).

Unfortunately the wrapped case 4 also leaks (we need to assume that any nested child (on any level?) can cause the leak)...

Are the leaked nodes really from first visit or actually from last visit?

To verify that, I created yet another case 4 variation with 500 items instead of 100 items.

The nodes leak from first visit as assumed (number of leaked nodes is depended of which case 4 variation was visited first).

Based on the last observation another (crazy?) idea could be:

  • We leak only on first route visit
  • What if the first route visit is kept very small (and so the leaked node count is very small)?
  • Can we have some redirect shenanigans?
  • E. g.:
    -- on first visit, the route root doesn't load children (v-if="firstVisit === false")
    -- on first visit, it only shows loading indicator
    -- on first visit, it redirects to a 'bounce'-route (after a delay? e. g. 500ms?)
    -- the bounce-route then redirect back to the 'from' location with param firstVisit="false"
    -- this time, the route root renders all childs

This kind of 'anti-leak'-wrapper could be abstracted / reused for all routes.

Thoughts?

@michaelperel
Copy link

@MarMun I haven't done as much investigation as you have, however, I am building an app where the landing page has a ton of DOM elements. When I profile the memory, after navigating around to different routes, the memory used by the first route is never reclaimed.

I don't know if the issue is in vue router or vue core, so I don't know if an 'anti-leak' wrapper would work. However, my experience is the same as the issue that was just linked, so it looks like it is a problem in core.

@MarMun
Copy link
Author

MarMun commented Sep 13, 2022

@michaelperel Thx for the update. I'll follow the mentioned issue even though I don't use v-if in my reproduction repo. Thumbs crossed it will solve this (my) issue as a 'side effect'.

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

3 participants