Subscribe on changes!

.unmount() memory leak

avatar
Dec 29th 2020

Version

3.0.4

Reproduction link

http://wagoon.demoeshop.net/test-remove-vue.html

Steps to reproduce

  1. click first button to create new Vue app
  2. click second button to unmout it (and delete all pointers to that app)
  3. call garbage collector in chrome dev tools
  4. create memory snapshot (app is still in memory linked from vue_app)

What is expected?

According to doc, .unmount() in VUE3 is replacement for $destroy in vue2 => this method should destroy all app data to avoid memory leaks Accordigt to 990803, unmounted app is not allegible for new mount(), so there is no need to store anything aboth it in memory (https://github.com/vuejs/vue-next/issues/1287#issuecomment-638252620)

What is actually happening?

App object stays linked from vue_app and cant be collected with garbage collector


Only solution i found is removing the target DIV from DOM I descrobed all steps in detail on stackoverflow post https://stackoverflow.com/questions/65475604/vue-js-3-unmount-and-memory-leak

avatar
Dec 29th 2020

Good catch, thanks for the report. PR is already up :)

avatar
Jan 14th 2021

Same problem. I need to dynamically create lots of vueApp in my project, and I use js-lru to cache, as they are evicted from the cache, I unmount and delete, but it doesn't help.

avatar
Jan 31st 2021

"According to doc"... The migration guide does not state that unmount() is a replacement, just that $destroy has been removed, and that Vue instances should not be managed manually. Which is weird, because sometimes you need to.

avatar
Feb 4th 2021

Hi in which version it will be this fix distributed?

avatar
Feb 4th 2021

Upcoming 3.0.6

avatar
Oct 16th 2022

I'm having exactly this problem, but in the server side... Once createSSRApp(App, fetchedData) for each request, it 's keeping me in memory the fetchedData.

App.js

import { createSSRApp } from 'vue';
import App from '../src/App.vue';
import PageStore from '../src/store/Brands.js';
import CommonStore from '../src/store/Common.js"';
import { createI18n } from 'vue-i18n';
import { createStore } from 'vuex';

export function getApp(fetchedData) {
  const modules = { ...CommonStore.modules, ...PageStore.modules };
  const storeOptions = { modules };
  const store = createStore(storeOptions);
  const app = createSSRApp(App, { fetchedData })
      .use(store)
      .use(i18n);
  return app;
}

entry-server.js

import { basename } from 'path';
import { getApp } from './app';
import { renderToString } from '@vue/server-renderer';
export async function render(data, ssrManifest, manifest, client, dist) {
    const { app } = getApp(data);
    const ctx = {};
    const html = await renderToString(app, ctx);
    const preloadLinks = ssrManifest ? renderPreloadLinks(ctx.modules, ssrManifest, dist) : '';
    const { entry, links } = manifest && client ? renderLinks(manifest, client, dist) : { entry: '', links: ''};
    return [ html, preloadLinks, entry, links ];
}

entry-client.js

import { getApp } from './app';
const data = window.__INITIAL_STATE__;
const { app } = getApp(data);
app.mount('#app');

As we can't unmount an app that it isn't mounted, the only solution that I found for the moment is create the createSSRApp once, and in each request overwrite the SSR context. But this is not a real solution, because we can have cross request state pollution...

Captura de Pantalla 2022-10-16 a la(s) 19 03 38

the image is a snapshot after 5 requests:

Array @3942577 Array @4259791 Array @4310869 Array @4422637 Array @4448451

The five Array objects are equal in each case.

Am I doing something wrong ?

avatar
Oct 17th 2022

I'm having exactly this problem, but in the server side... Once createSSRApp(App, fetchedData) for each request, it 's keeping me in memory the fetchedData.

App.js

import { createSSRApp } from 'vue';
import App from '../src/App.vue';
import PageStore from '../src/store/Brands.js';
import CommonStore from '../src/store/Common.js"';
import { createI18n } from 'vue-i18n';
import { createStore } from 'vuex';

export function getApp(fetchedData) {
  const modules = { ...CommonStore.modules, ...PageStore.modules };
  const storeOptions = { modules };
  const store = createStore(storeOptions);
  const app = createSSRApp(App, { fetchedData })
      .use(store)
      .use(i18n);
  return app;
}

entry-server.js

import { basename } from 'path';
import { getApp } from './app';
import { renderToString } from '@vue/server-renderer';
export async function render(data, ssrManifest, manifest, client, dist) {
    const { app } = getApp(data);
    const ctx = {};
    const html = await renderToString(app, ctx);
    const preloadLinks = ssrManifest ? renderPreloadLinks(ctx.modules, ssrManifest, dist) : '';
    const { entry, links } = manifest && client ? renderLinks(manifest, client, dist) : { entry: '', links: ''};
    return [ html, preloadLinks, entry, links ];
}

entry-client.js

import { getApp } from './app';
const data = window.__INITIAL_STATE__;
const { app } = getApp(data);
app.mount('#app');

As we can't unmount an app that it isn't mounted, the only solution that I found for the moment is create the createSSRApp once, and in each request overwrite the SSR context. But this is not a real solution, because we can have cross request state pollution...

Captura de Pantalla 2022-10-16 a la(s) 19 03 38

the image is a snapshot after 5 requests:

Array @3942577 Array @4259791 Array @4310869 Array @4422637 Array @4448451

The five Array objects are equal in each case.

Am I doing something wrong ?

Nevermind the problem was vuex.

avatar
Oct 18th 2022

Hi @andoniabedul , How did you figure out that the problem was Vuex?

What did you do to solve the issue in that case?

Thanks in advance

avatar
Nov 28th 2022

Hi @gquinteros93 I solved the problem adding a devtools: false in the app.use of vuex when is production.

Sorry for the delay answering I didn't read the notifications.

avatar
Nov 28th 2022

I would add that it's quite important that you use the production as NODE_ENV. In our case we were using prod and are some important differences on how Vue act in base on this variable.

For example, if you are not using production and you use prod, Vue will inject the HMR Plugin in each request.