Vue-router: Memory leaks when switching routes

Created on 22 Jan 2017  ·  21Comments  ·  Source: vuejs/vue-router

// Edit: Seems like this has something to do with trying to use the .vue files only. Using components defined in js is working fine..
// Yeah, what I wasn't mentioning was, is that I was running this on windows. Project works when building on linux. Either webpack or vuerouter is messing it up on windows. More likely webpack.

Greetings. I have been aching my head with this for a while.

I got a lot of memory leaks in my bigger project, so I tried getting rid of all the extra libraries and come to conclusion that there has to be something wrong with either vue-router, vue itself or webpack. Vue-router seems most likely candidate right now.

I'm running vue-router in vuejs-template's webpack-template, with hot-reload dev-build (Problem also applies to the distrubtion build). I am using .vue standalone files.

I have two separate routes. I have buttons in main vue instance for switching between the routes in addition to the router-view component.

I have some very basic rudimentary elements and components inside these views.

Using chrome's profiler, I use heap snapshots and filter these snapshots for "Vue*" classes.

Every time I switch between routes, new VueComponents and Vue$3 objects get added to the heap. Existing ones don't seem to be removed. These never seem to be garbage collected.

Any idea what's going on here?

bug

Most helpful comment

@yyx990803 That's probably something you have to look at. Might also be related to core rather than router.

All 21 comments

Please follow the Issue Reporting Guidelines and provide a minimal JSFiddle or JSBin containing a set of reproducible steps that can lead to the behaviour you described.

Aye, excuse me for not providing one in initial post. I retract my statement that this only happens on windows. I don't know how I interpreted it working on linux yesterday, but it doesn't.

Since this could likely be something to do with my webpack build, although I haven't changed much, here's the the whole project

https://github.com/badpunman/memoryleakproject

Setup

git clone https://github.com/badpunman/memoryleakproject
npm install
npm run dev

Reproduction steps

Using chrome, go to localhost:8080
F12, Profiles-tab, take heap shot.
Filter result with 'Vue'.
There are 2 VueComponent objects and 7 Vue$3's.

Switch between the views by clicking on "Hello" and "home" buttons a couple of times.

Take another heapshot.
Filter result with 'vue'
There are loads more VueComponents and vue$3s.
These never get released.

This seems to be an issue with .vue single file components only that are referenced in src/router.js

Since this could likely be something to do with my webpack build, although I haven't changed much, here's the the whole project

In that case, can you make sure it is not by first trying to reproduce it on a jsfiddle, please?

In that case, can you make sure it is not by first trying to reproduce it on a jsfiddle, please?

Sorry, I'm not sure how to do that, considering issue concerns .vue single file components. I don't imagine jsfiddle supports webpack and vue-loader.

I meant to be sure it's not a vue-router problem, so without using vue files 😄
Why do you think it's a vue sfc issue?

Ah, yes, well, without using .vue files, instead using components defined in separate .js or in the same file as the src/router.js, it works. Providing jsfiddle of it would be rather pointless if everything is just working.

I just don't understand at all what the mechanism is at play here that makes it so different for .vue. I would presume the vue-router stores the vue-loader's output once and wouldn't spam multiple instances of everything when switching the views.

Ah, yes, well, without using .vue files, instead using components defined in separate .js or in the same file as the src/router.js, it works.

Ok! Sorry, I didn't understood you tested that already.
I'll give it a check

Aye, thanks for checking.

It seems like I can use .vue files without memory leaks as long as I don't assign them directly to the router. It's rather silly, but I guess I'll ensure my router views are declared as non .vue for now.

Sorry for the delay. I got a hard time looking at what you did. There was still a lot of code that didn't affect the issue. It seems to be related to an input having a v-model.
There are things you shouldn't be doing on your Vue files, like creating global components with Vue.component

I couldn't narrow down the issue yet to verify it. So I just wanted to keep you updated

It seems to be related to an input having a v-model.

Oh. That's strange. Seems to be so.

Seems to also trigger when adding @click event handler in a vue file that is a direct route component.

Since both v-model and @click add event handlers to the dom elements, I presume this might be a case of event handlers not being cleaned up from the dom elements upon removal. No idea what the behind the scene mechanisms are for Vue or Vue-router for disposing of components though.

Did you manage to boil down the repro? I'll gladly look at an updated version

Nah, sorry. Been a tad busy as of late. Not sure how much more I can really boil it down.

Results of more digging in to the problem:

It seems like when switching route, the previous Dom-node is retained in vm.$options._refElm. If the dom has those event listeners in the vue components, vue components are then also retained.

Now, I have no insight in to what _refElm is actually used for, or how to fix this.

I wonder if this is more a vue bug, or does vue-router somehow misuse something with vue?

Yeah, there can be a leak if events are not cleaned when reusing elements

Well, here's a concise example.

Leaking happens when toggling between Foo and Bar router views. Both of these are simple input elements with v-model watchers.

The state cleans up when moving in to out of Dummy route, which has no listeners in template.

https://github.com/badpunman/memoryleakproject/blob/master/src/router.js

Here's a look a the retainers. Deepest Vuecomponent is the current route's component. It holds reference to earlier component's Dom via $options._refElm, which has listeners in the previous vuecomponent, and then the cycle repeats for further components that have been switched in router.

image

@yyx990803 That's probably something you have to look at. Might also be related to core rather than router.

Was fixed in Vue 2.2 core in #4990

I seem to be having this same problem in 2.2.3, 2.2.4 and 2.2.5. It seems to be related to adding an @click or v-model, as well as related to vm.$options._refElm. The leak appears to be related to changing routes.

I've been trying to fix the memory leak for a few days now but having no luck. This is a real problem since the leak seems to carry over into the server-side rendering, increasing the memory use of the Node app until it slows to a crawl.

@jazoom please use a separate issue with repro and more information.

I'm trying to create a minimal reproduction for you but it's not easy to pin down where the actual problem is since it happens only under certain circumstances. I haven't yet worked out the full pattern.

Was this page helpful?
0 / 5 - 0 ratings