Join GitHub today
GitHub is home to over 36 million developers working together to host and review code, manage projects, and build software together.Sign up
Memory leak in Framework7 Vue navigation #2848
Describe the bug
VueComponents aren't being garbage collected after you navigate off a page.
While navigating back and forth between two pages in an F7 Vue app, I expect memory usage to stay within constant bounds. I do not expect the upper bound to continually group.
Memory usage grows continuously while navigating between pages in an F7 Vue app. It appears that VueComponents are not being GC'd after destroyed().
I've reproduced this using:
It happens with both versions 3.4 (sample config) and 3.5.2 (current). It also happens with the real F7 Vue app I am working on. Navigation eats up memory and makes the UI increasingly sluggish. Eventually, the browser throws an out of memory exception.
I have reproduced the memory leak in sample apps "F7 Vue Simple", F7 Vue Webpack", and "F7 React". This suggests to me that the problem is between the router and components, whether Vue or React, and that the router holds a reference to component objects even after the component has been destroyed.
Even a destroyed component cannot be garbage collected until all references to it have been removed from other reachable objects in the JS memory graph. The router is always reachable, so if it has a reference to a destroyed component, the component will never be GC'd. I'm guessing there might be an event emitter or an array somewhere in the router that still has a reference to the destroyed component object after you navigate off the page.
I don't know the router code well enough to know where to look, though. Any suggestions?
Maybe you can help? As i can't understand where it comes from, double checked all the stuff.
Phenome (Vue/React) Router is here https://github.com/framework7io/framework7/blob/master/src/phenome/utils/components-router.js. You may check page
Pages itself handled in View component https://github.com/framework7io/framework7/blob/master/src/phenome/components/view.jsx
Router and View communicate with each other with events emitter https://github.com/framework7io/framework7/blob/master/src/phenome/utils/events.js
These are all files that can contain reference to components
I have made very limited progress so far, despite substantial effort.
I can consistently fix the sample app About page component memory leak by not rendering the on.click handler of the f7-link component. Why this handler keeps the components from getting garbage collected I don't know. Other components on the About page also render click handlers (like navleft), but for whatever reason these--on their own--don't cause the page and sub-components to stay in memory.
In the heap dump I see a lot of native_binds and feedback_cells in the retainers. This and the fact that the memory leaks appear to be all or nothing suggest to me that it might be one of the more exotic closure scope leaks, as described here:
But if that is the case, I haven't figured out how to identify the source from the heap dump or from trial & error.
Next I will try to reproduce the bug in plain VueJs, and use the results of that to further bisect the problem. This is not my wheelhouse, so it's be quite a learning curve to even get this far.
Unfortunately, if we can't solve this problem, we'll have to find something other than F7 for our app. We've already replaced most of our nested components with plain F7 markup. This has reduced the magnitude of the memory leak and improved overall performance (less CPU usage rendering pages, fewer objects on the JS heap). But there's still a steady leak, and we need our app to be stable during continuous heavy use, so any level of memory leak is going to be a problem for us. It's too bad, because otherwise the framework has been great.
Interesting note: Chrome, Firefox and Safari all exhibit this memory leak. Edge does not. This is another reason I think it might be an exotic closure bug, since how closures capture over and share variables in the same lexical scope is an implementation detail and could vary by JS engine.
Thanks! Spent a while with this today. Don’t understand why is this happening but looks like for some reason Vue just doesn’t remove its “native” event listeners, so I reworked all click handlers to manual el.add/removeEventListeners. Situation became a much better, at least on many Ks pages amount of Vue components is not increasing. But looks like issue is still with inputs’ events. Will keep investigating
This reminds me of a VueJS issue regarding click bindings, which appears consistent with your work-around to explicitly remove native listeners: vuejs/vue#7086
Evan You closed the issue with this comment:
I don't really understand his explanation, except that he expects to native listeners to be cleaned up automatically as long as other things elsewhere are cleaned in line with VueJS expectations.
Hello Vladimir, do you have any updates on this issue?
I tested release 3.6.1 in the F7 Vue sample app. Navigating to and from the About page no longer causes VueComponent leaks. The About page instantiates 11 new VueComponents, and these are now cleaned up each time you navigate off. Great!
However, navigating to and from the Form page does continue to causes memory leaks. The Form page instantiates 96 new VueComponents, and these are not cleaned up after you navigate off. In the memory dump, I see f7Page.methods.onPageAfterOut, F7ListItemContent.render and F7Range.render as some of the most frequent retainers.
I keep researching. But issues on that pages is because we still have same Vue’s native input events (onChange/Focus/Blur, etc.) I tried to remove and situation becomes better, but I don’t feel it is right move. If we move all listeners to custom addEventListener then it will break React, because it requires those events to be specified. Need to find the origin of the issue
@mmis1000 will check the slots thing
Vladimir, just to clarify where things stand now:
a) you are confident that you will fix the memory leak, you just need to find the best approach.
-- or --
b) you may or may not be able to fix the memory leak, depending on whether you find the origin of the issue.
I spent a few days trying to find the origin of the issue, but with no luck. Since then I have put the issue aside, hoping that you might be able to solve it. Now we have some decisions to make regarding our own software. We can't release it with a memory leak, so this issue here will become a blocker sooner or later. Maybe we could crowd-source the answer? I have spare karma on SO to place a bounty, if there's a suitable question.