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

.unmount() memory leak #2907

Closed
wagoon opened this issue Dec 29, 2020 · 10 comments · Fixed by #2909
Closed

.unmount() memory leak #2907

wagoon opened this issue Dec 29, 2020 · 10 comments · Fixed by #2909
Assignees
Labels
🍰 p2-nice-to-have Priority 2: this is not breaking anything but nice to have it addressed. 🐞 bug Something isn't working

Comments

@wagoon
Copy link

wagoon commented Dec 29, 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 (#1287 (comment))

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

@LinusBorg LinusBorg added 🐞 bug Something isn't working 🍰 p2-nice-to-have Priority 2: this is not breaking anything but nice to have it addressed. labels Dec 29, 2020
@LinusBorg LinusBorg self-assigned this Dec 29, 2020
@LinusBorg
Copy link
Member

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

@happydoop
Copy link

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.

@danielgindi
Copy link

"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.

LinusBorg added a commit that referenced this issue Feb 3, 2021
… unmount (close #2907) (#2909)

close #2907

Co-authored-by: Thorsten Luenborg <t.luneborg@googlemail.com>
@wagoon
Copy link
Author

wagoon commented Feb 4, 2021

Hi
in which version it will be this fix distributed?

@LinusBorg
Copy link
Member

Upcoming 3.0.6

@andoniabedul
Copy link

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 ?

@andoniabedul
Copy link

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.

@gquinteros93
Copy link

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

@andoniabedul
Copy link

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.

@andoniabedul
Copy link

andoniabedul commented Nov 28, 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.

@github-actions github-actions bot locked and limited conversation to collaborators Sep 17, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.