Home > Net >  Why does closure cause memory leak in JavaScript in this case?
Why does closure cause memory leak in JavaScript in this case?

Time:07-06

I have this following code snippet:


function outer() {
  let a

  return function inner() {
    
     a = new Uint8Array(100000)
     const b = new Uint16Array(100000)
  };
};
const fn = outer(); 
fn()

Took one heap snapshot, ran it in a about:blank page in chrome 103.0.5060.114, and took a second snapshot. Comparing the two snapshots, I found that in the second snapshot, there is one more Uint8Array. That means a is retained in memory, so it leaked. But I can't find any Uint16Array so b didn't leak. enter image description here enter image description here

But I couldn't figure out why that Uint8Array is leaking because it doesn't seem like I can still reference it outside the outer function. So According to the garbage collection algorithm, it should have been collected by now.

Haven't tested this in other browsers or in Node.

CodePudding user response:

@ITgoldman's explanation is right: a is retained because inner uses it, and fn === inner, and fn is still reachable. This is not a leak.

a can be reached again after fn() finishes simply by calling fn() again. b is just a local variable, so it goes out of scope when fn() returns.

An example how this is useful/required: consider the following slight modification of your snippet:

function outer() {
  let a = 0;

  return function inner() {
    a  ;
    console.log(`counter is now ${a}`);
  };
};
const fn = outer(); 
fn()
fn()
fn()

Clearly, a should remain alive from one invocation of fn() to the next, so it can't be collected.

It doesn't matter whether inner/fn reads from a, writes to a, or does both. As long as it uses a in any way, a will remain alive as long as fn is alive. This makes sense because there could be more than one function referring to it: you could have inner1 allocating new arrays and storing them in a, and inner2 doing stuff with these arrays.

(This is not a V8 question; all spec-compliant JavaScript engines are required to behave like this.)

CodePudding user response:

Usually, a Lexical Environment is removed from memory with all the variables after the function call finishes. That’s because there are no references to it. As any JavaScript object, it’s only kept in memory while it’s reachable.

However, if there’s a nested function that is still reachable after the end of a function, then it has [[Environment]] property that references the lexical environment.

You can more about how garbage collection works in closures here.

  • Related