Skip to content

router.push memory leak #3054

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
ChuckFields opened this issue Dec 5, 2019 · 5 comments
Closed

router.push memory leak #3054

ChuckFields opened this issue Dec 5, 2019 · 5 comments

Comments

@ChuckFields
Copy link

ChuckFields commented Dec 5, 2019

Version

3.1.3

Reproduction link

https://chuckfields.github.io/

Steps to reproduce

Note: this is a separate memory leak from the one I posted the other day, but has a very similar reproduction setup.

Clicking on the about tab (router link) will create a javascript object with the class 'LingeringObject' and put it in the store under lingeringObj. Clicking on the home tab (router link) destroys the about component and resets lingeringObj to null- this causes the LingeringObject to get removed by the garbage collector- this is all fine, and can be tested by taking a heap snapshots in chrome. Now, the memory leak that I've noticed happens when you use the 'Go Home' button on the about tab. This uses router.push() instead of a <router-link>. After clicking the Go Home button, if you take a snapshot, you'll notice the LingeringObject is still in memory. What's even weirder is when you inspect the object to see what is still holding onto a reference of it- it's showing HTMLButtonElement, which is the Go Home button. Very peculiar. According to the vue router documentation: "...clicking <router-link :to="..."> is the equivalent of calling router.push(...)."

Here is the source code for the reproduction link:
https://github.com/ChuckFields/VueRouterLingeringObject

What is expected?

The LingeringObject to get removed from memory when using router.push()

What is actually happening?

It's staying in memory

@ChuckFields
Copy link
Author

I seem to have found another very similar memory leak, but this one affects even the <router-link>.

Here is the source code for this memory leak: https://github.com/ChuckFields/VueRouterLingeringObject2

For this one, the 'LingeringObject' has children ('LingeringChild'). The store has a getter 'lingeringObjChildren' to get these children. In the About.vue we watch for the lingeringObj to change. In the watcher, if we just reference getters.lingeringObjChildren, we now have a memory leak, and going back to the Home page will result in the LingeringChild to remain in memory.
I haven't spent too much time figuring out exactly what is causing this one, but taking out the reference to getters.lingeringObjChildren in the watcher prevents the memory leak from happening. This is similar to the router.push memory leak in that, when I inspect the LingeringChild in the heap snapshot to see whats still referencing it, it is says HTMLAnchorElement, which is the router-link hyperlink.

@posva
Copy link
Member

posva commented Dec 6, 2019

I don't see any leak with the first example: memory stays stable

When reporting a memory leak, as said in the other issue, please keep it minimal, if the second one is related to watch, then don't add a router and store. Also, if it's on an HTML file (you can share just the content) it's much easier to check for us and play around locally

@posva posva closed this as completed Dec 6, 2019
@ChuckFields
Copy link
Author

@posva This is NOT an accumulating memory leak, in other words, doing the steps to reproduce this multiple times will not result in multiple objects remaining in memory. However it is still a memory leak. The LingeringObject should not be in memory at all when going back to the home page, but it is. Nothing in the code I've written maintains a reference to that object. This is a very scaled back example of a large enterprise application in which the object that remains in memory is very large ~500mb.

For these examples, I couldn't find a way to reproduce these issues without using the router and store. The second example is related to the watch, but it watches a value in the store, and needs the router-link to reproduce. I'm not sure what I could have done to minimize these examples. I created repositories with the full source code so you can run it locally and see the memory leak in action.
I really think you should consider reopening this issue.

@ChuckFields
Copy link
Author

@posva, I've found a workaround:

  beforeRouteLeave(to, from, next) {
    this.$store.dispatch("setLingeringObj", null);
    this.$nextTick(() => {
      next();
    });
  },

By wrapping the next() call with a nextTick in beforeRouteLeave, we fix the issue for both router.push and router-link. I do still think there is a real bug here, and I think this workaround can give some clues as to what exactly might be causing this. If I have time, I will investigate further.

@garymcm
Copy link

garymcm commented May 24, 2021

@ChuckFields Thank you for this workaround. I have been looking at this issue off and on for a few months. Inspecting the memory allocation, I was seeing the Component memory release then almost instantly re-inflate during navigation to a new component and lots of Detached Elements. Your workaround has resolved this for now. I don't know if it's significant, but I use BootstrapVue b-table and even after clearing state, I also need to manually update the b-table component to see the memory released. I feel like this points to the shadow DOM not updating correctly.

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