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

Memory leak: onclick handler holds onto detached DOM nodes #9346

Closed
nWacky opened this issue Oct 5, 2023 · 6 comments
Closed

Memory leak: onclick handler holds onto detached DOM nodes #9346

nWacky opened this issue Oct 5, 2023 · 6 comments

Comments

@nWacky
Copy link

nWacky commented Oct 5, 2023

Vue version

3.3.4

Link to minimal reproduction

https://github.com/nWacky/nuxt-15239/tree/vue-only-reproduction

Steps to reproduce

In App.vue there is a toggle button and TheList component

TheList component renders a list of child components TheListItem

When clicking the toggle button, about 900kb of ram is allocated to show TheList.

When clicking the button again, TheList is unmounted.
900kb is not cleared up after forcing gc in Memory inspector in Chrome.

Clicking the toggle button again allocates another 900kb for TheList.

What is expected?

When TheList is unmounted, the ram should be cleared

I think this is a memory leak

What is actually happening?

Memory inspector in Chrome points to context somewhere in createInvoker().

Removing @click="onClickButton" from TheListItem reduces leaked memory from 988Kb to 160Kb per mount/unmount cycle.

Perhaps, onclick listener is never unmounted, and is holding onto detached DOM nodes

System Info

Compiled with node `v18.17.0`
Tested in Chrome `117.0.5938.149` in incognito mode, without vue devtools plugin

Any additional comments?

Possibly related issue: nuxt/nuxt#15239

@edison1105
Copy link
Member

try #5256 (comment)

@nWacky
Copy link
Author

nWacky commented Oct 8, 2023

Adding template: { compilerOptions: { hoistStatic: false } } to vite.config.ts helped a bit

Previously, ~960kb was allocated per mount/unmount cycle.

Memory usage screenshot without hoist static

With hoistStatic: false there are ~890kb allocated per mount/unmount cycle.

Memory usage screenshot with `hoistStatic: false`

When the code is modified to manually mount/unmount onclick listener (without using @click), the retained memory drops to 137kb per mount/unmount cycle.

Manual mounting/unmounting onclick listener code example + memory screenshot

Code example:

<!-- Part of `src/components/TheListItem.vue` -->
<script setup lang="ts">
const onClickButton = () => { alert("clicked button"); };

const theButton: Ref<HTMLButtonElement | null> = ref(null);

onMounted(() => {
  if (theButton.value) {
    theButton.value.onclick = onClickButton;
  }
});

onBeforeUnmount(() => {
  if (theButton.value) {
    theButton.value.onclick = null;
  }
});
</script>

<template>
  <div>
    <button ref="theButton">Button</button>
  </div>
</template>

Retained memory in Allocation instrumentation on timeline in Chrome:

@edison1105
Copy link
Member

This is a Chrome bug. see https://bugs.chromium.org/p/chromium/issues/detail?id=1213045
remove loading="lazy" and there will be no memory leak.

<img :src="src" :title="alt" loading="lazy" />
+<img :src="src" :title="alt"/>

@nWacky
Copy link
Author

nWacky commented Oct 9, 2023

That explains 137kb leaking when mounting/unmounting a component

My primary question, however, is mostly about memory not being freed when using @click

When manually adding onclick handler in onMounted and removing in onBeforeUnmount,
137kb is never freed. (code example in previous comment)

But when using @click (like in the example below), 890kb is never freed

<script setup lang="ts">
const onClickButton = () => { };
</script>

<template>
  <div>
    <button @click="onClickButton">Button</button>
  </div>
</template>

@edison1105
Copy link
Member

edison1105 commented Oct 9, 2023

My primary question, however, is mostly about memory not being freed when using @click

Please read #5363 first

I removed loading="lazy" and set hoistStatic: false. No more detached DOM nodes in memory.

@nWacky
Copy link
Author

nWacky commented Oct 9, 2023

I checked it again, it seems to be caused by loading="lazy" on <img />

I guess this is a duplicate of #5363 then

Thank you 🙂

@nWacky nWacky closed this as completed Oct 9, 2023
@github-actions github-actions bot locked and limited conversation to collaborators Oct 25, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants