Javascript Memory Leak From Closure Lexical Environment
Solution 1:
The primary answer is that in your second code block, no direct reference to either of the closures (closure1
or someMethod
) survives the return of outer
(nothing outside outer
refers to them), and so there's nothing left that refers to the context where they were created, and that context can be cleaned up. In your second code block, though, a direct reference to someMethod
survives the return, as part of the object that you're assigning to aThing
, and so the context as a whole cannot be GC'd.
Let's follow what happens with your first block:
After the first execution of outer
, we have (ignoring a bunch of details):
+−−−−−−−−−−−−−+ aThing−−−−−>| (object #1) | +−−−−−−−−−−−−−+ | str: ... | +−−−−−−−−−−−−−−−−−−−−+ | someMethod |−−−−>| (context #1) | +−−−−−−−−−−−−−+ +−−−−−−−−−−−−−−−−−−−−+ | something: null | | closure1: function | +−−−−−−−−−−−−−−−−−−−−+
after the second execution:
+−−−−−−−−−−−−−+ aThing−−−−−>| (object #2) | +−−−−−−−−−−−−−+ | str: ... | +−−−−−−−−−−−−−−−−−−−−+ | someMethod |−−−−>| (context #2) | +−−−−−−−−−−−−−+ +−−−−−−−−−−−−−−−−−−−−+ +−−−−−−−−−−−−−+ | something |−−−−>| (object #1) | | closure1: function | +−−−−−−−−−−−−−+ +−−−−−−−−−−−−−−−−−−−−+ | str: ... | +−−−−−−−−−−−−−−−−−−−−+ | someMethod |−−−−>| (context #1) | +−−−−−−−−−−−−−+ +−−−−−−−−−−−−−−−−−−−−+ | something: null | | closure1: function | +−−−−−−−−−−−−−−−−−−−−+
after the third execution:
+−−−−−−−−−−−−−+ aThing−−−−−>| (object #3) | +−−−−−−−−−−−−−+ | str: ... | +−−−−−−−−−−−−−−−−−−−−+ | someMethod |−−−−>| (context #3) | +−−−−−−−−−−−−−+ +−−−−−−−−−−−−−−−−−−−−+ +−−−−−−−−−−−−−+ | something |−−−−>| (object #2) | | closure1: function | +−−−−−−−−−−−−−+ +−−−−−−−−−−−−−−−−−−−−+ | str: ... | +−−−−−−−−−−−−−−−−−−−−+ | someMethod |−−−−>| (context #2) | +−−−−−−−−−−−−−+ +−−−−−−−−−−−−−−−−−−−−+ +−−−−−−−−−−−−−+ | something |−−−−>| (object #1) | | closure1: function | +−−−−−−−−−−−−−+ +−−−−−−−−−−−−−−−−−−−−+ | str: ... | +−−−−−−−−−−−−−−−−−−−−+ | someMethod |−−−−>| (context #1) | +−−−−−−−−−−−−−+ +−−−−−−−−−−−−−−−−−−−−+ | something: null | | closure1: function | +−−−−−−−−−−−−−−−−−−−−+
You can see where this is going.
Since the second block never retains a reference to closure1
or someMethod
, neither of them keeps the context in memory.
When originally answering your question in 2015 I was slightly surprised that V8 (Chrome's JavaScript engine) didn't optimize this leak away, since only someMethod
is retained, and someMethod
doesn't actually use something
or closure1
(or eval
or new Function
or debugger
). Although in theory it has references to them via the context, static analysis would show that they can't actually be used and so could be dropped. But closure optimization is really easy to disturb, I guess something in there is disturbing it, or that the V8 team found that doing that level of analysis wasn't worth the runtime cost. I do recall seeing a tweet from one of the V8 team saying that it used to do more closure optimization than it does now (this edit is in Sep 2021) because the trade-off wasn't worth it.
Post a Comment for "Javascript Memory Leak From Closure Lexical Environment"