Skip to content Skip to sidebar Skip to footer

Javascript Memory Leak From Closure Lexical Environment

I am trying to understand why the following code causes a memory leak Here is the timeline showing memory increasing from Google Chrome: but this code which is a very slight var

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"