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

Possible memory leak #3668

Open
Z2Y opened this issue Aug 13, 2022 · 9 comments
Open

Possible memory leak #3668

Z2Y opened this issue Aug 13, 2022 · 9 comments
Labels

Comments

@Z2Y
Copy link

Z2Y commented Aug 13, 2022

Describe the bug
When i use preact with a virtualized list, I found memroy not gabage collected. this may cased by a chrome issue:
The MouseEventManager keeps removed DOM nodes alive until subsequent click https://bugs.chromium.org/p/chromium/issues/detail?id=1177010
if there is no click event for a while , (such as scrolling infinite card list ), the memory usage grows until next click.

@marvinhagemeister marvinhagemeister added the needs-more-info The issue doesn't contain enough information to be able to help label Aug 14, 2022
@marvinhagemeister
Copy link
Member

marvinhagemeister commented Aug 14, 2022

Please share more information about your setup. We cannot see your screen and don't know what your code looks like, so it's pretty much impossible for us to provide help with the issue you're seeing. Specifically the virtual list implementation that's used in your project and how you're using it might be of interest.

@Z2Y
Copy link
Author

Z2Y commented Aug 14, 2022

1660469565(1)

the event handler hold the ref of a unmountd vnode, ant the vnode hold the parent vnode. if the detached dom not gabage collected , it will cause memory leak.

@JoviDeCroock
Copy link
Member

JoviDeCroock commented Aug 14, 2022

From the linked issue this is a chrome bug and not a Preact one, there's not much we can do about the implementation of a browser event handler as this would occur in any library. The screenshot attached also does not seem to relate to the bug as this is a VDOM structure.

The event-handler in question is a native one as we aren't creating our own events so the only things interacting here should be your callback and the browser event-handler.

If this is a Preact bug could you show us how this related to the chrome bug or how the screenshot is relevant to what you are saying - currently you say that the event handler holds the ref of an unmounted vnode, do you mean that the vnode holds a reference to the dom-node which in turn has event-handlers? Theoretically this makes sense as a virtualization library usually won't unmount dom-nodes rather than just hiding them so I don't think it's a memory leak as much as an implementation detail in the virtualization library.

Do note that the above hinges on assumption that I made, if these are incorrect please provide more information

@Z2Y
Copy link
Author

Z2Y commented Aug 15, 2022

I managed to create a demo to reproduce it https://codesandbox.io/s/preact-vlist-demo-ehphz9
steps: Add some items and then scroll up and down, As the chrome devetool show , the number of detached HtmlButtonElement grows up.
WeChat0a55e3857971618997c10743f53c67a5

same code using react has no such issue https://codesandbox.io/s/react-vlist-demo-f3egjs

@Z2Y Z2Y changed the title Posible memory leak Possible memory leak Aug 17, 2022
@developit
Copy link
Member

@Z2Y this happens because SomeButtonItem closes over props.children, which retains it. The issue is already fixed in Preact 11, where props.children is a pure JSX element with no tree structure:
https://codesandbox.io/s/preact-vlist-demo-forked-o26dg7?file=/src/index.js

In the meantime, here's a workaround that adds some additional unmount cleanup that likely addresses the issue:

import { options } from 'preact';

let oldUnmount = options.unmount;
options.unmount = (vnode) => {
    if (oldUnmount) oldUnmount(vnode);
    if (vnode.__e) vnode.__e.l = undefined;
    if (vnode.__c) vnode.__c.__H = undefined;
    vnode.__ = vnode.__k = vnode.props = undefined;
};

@Z2Y
Copy link
Author

Z2Y commented Aug 18, 2022

@developit Dose the workaround has other side effects? Since there are some work after options.unmount.

preact/src/diff/index.js

Lines 499 to 527 in 1cb4b38

if ((r = vnode.ref)) {
if (!r.current || r.current === vnode._dom) applyRef(r, null, parentVNode);
}
if ((r = vnode._component) != null) {
if (r.componentWillUnmount) {
try {
r.componentWillUnmount();
} catch (e) {
options._catchError(e, parentVNode);
}
}
r.base = r._parentDom = null;
}
if ((r = vnode._children)) {
for (let i = 0; i < r.length; i++) {
if (r[i]) {
unmount(r[i], parentVNode, typeof vnode.type != 'function');
}
}
}
if (!skipRemove && vnode._dom != null) removeNode(vnode._dom);
// Must be set to `undefined` to properly clean up `_nextDom`
// for which `null` is a valid value. See comment in `create-element.js`
vnode._dom = vnode._nextDom = undefined;

Currently I add these vnode to a set then do the cleanup later.

options.unmount = (vnode) => {
    if (oldUnmount) oldUnmount(vnode);
    unmountedVnodes.add(vnode); // then do cleanup in a setInterval
};

@developit
Copy link
Member

@Z2Y the workaround won't break anything, I only have it clearing properties that Preact ignores during unmounting.

Your Set solution is good though, you could iterate over the properties of each and set them to undefined.

@JoviDeCroock
Copy link
Member

@developit that workaround might be dangerous in suspense scenario's no?

@JoviDeCroock JoviDeCroock removed the needs-more-info The issue doesn't contain enough information to be able to help label Sep 2, 2022
@Z2Y
Copy link
Author

Z2Y commented Sep 13, 2022

I upgrade to latest preact v10.11.0, but the problem still exist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants