preact icon indicating copy to clipboard operation
preact copied to clipboard

Preact causes memory leak on useState?

Open avkonst opened this issue 4 years ago • 6 comments

I have spotted memory growth in a performance benchmark. I have compared the same benchmark app with react. React clears memory when a component with useState is unmounted. Preact holds the state value after unmount:

image

I am not sure about the cause and if it is expected behaviour, but it definitely spoils performance comparison results for Preact and probably a red flag, which is worth investigating further.

Here is how to reproduce it:

  • Clone https://github.com/avkonst/js-framework-benchmark

  • npm install

  • npm start (notice webserver started, if it does not start check prerequisites here: https://github.com/avkonst/js-framework-benchmark)

  • cd ./frameworks/keyed/preact-hookstate

  • npm install

  • npm run build-prod

  • open crome at http://localhost:8080/frameworks/keyed/preact-hookstate/

  • open development tools, memory tab image

  • take heap snapshot using chrome devtools image

  • click "Create 1000 rows" image

  • take heap snapshot using chrome devtools

  • click "Clear" image

  • take heap snapshot using chrome devtools

  • go to snapshot 2 and setup comparison with snapshot 1 image

  • notice an object with 2001 allocations (this is the Hookstate state link referencing the state variable placed into useState)

  • go to snapshot 3 and setup comparison with snapshot 2 image

  • notice only 1 state link deleted, although it is expected 2001 are deleted, at least eventually.

  • retantion tree for the state variable from useState() call is looking strange, I can not interpret much here: image

I can see that React also retains states after component is unmounted, but it get's cleaned up eventually. I.e. if I run create/clear many many times in a loop, React releases states and you can see that about 1000 states are retained (about 4MB). If I run it many times with Preact, it retains all states from past mounted and unmounted components (I even reached 70MBs by doing this in cycle).

avkonst avatar Feb 12 '20 21:02 avkonst

BTW, if you are interested in to see how React compares with Preact on the benchmark with Hookstate, have a look here: https://github.com/avkonst/hookstate/issues/14#issuecomment-585023323

avkonst avatar Feb 12 '20 21:02 avkonst

Excellent analysis. A few initial guesses at the culprit here:

  • circular references in the virtual DOM tree (vnode._parent 🔁 vnode._children[0])
  • component._hooks being attached to those vnodes as vnode._component.__hooks

developit avatar Feb 13 '20 01:02 developit

Hey @avkonst

Thanks for the awesome explanation, we can validate point two made in the above reply by doing

yarn add https://github.pika.dev/preactjs/preact/pr/2337

I'll try this tonight or if you have time before I do feel free to report it! Thanks again for this awesome issue

JoviDeCroock avatar Feb 13 '20 13:02 JoviDeCroock

Hmm another circular reference is: (assuming c.__hooks._list[0] is the useState hook) component.__hooks._list[0]._component == component. Could that impact this?

andrewiggins avatar Feb 17 '20 00:02 andrewiggins

@kiranmarshall Please file a new issue. The problem you're describing is unrelated to memory growth.

marvinhagemeister avatar Oct 14 '23 08:10 marvinhagemeister

@marvinhagemeister I ended up finding the issue and it was a totally unrelated. PEBCAK-ing hard, as usual. Thanks anyway!

kiranmarshall avatar Oct 14 '23 09:10 kiranmarshall