[Memory leak] 3. Garbage collection in Closure

Example code:

function createIncrease() {
  const doms = new Array(100000).fill(0).map((_, i) => {
    const dom = document.createElement('div');
    dom.innerHTML = i;
    return dom;
  });

  function increase() {
    doms.forEach((dom) => {
      dom.innerHTML = Number(dom.innerHTML) + 1;
    });
  }

  return increase;
}

const increase = createIncrease();
const btn = document.querySelector('button');
btn.addEventListener('click', increase);

 

What is garbage?

Is domsvariable a garbage in above code?

No. It's not, because code need to use it. 

 

Then how about this code? nums

<script>
  const nums = [1, 2, 3, 4, 5];
  const sum = nums.reduce((total, num) => total + num, 0);

  console.log(sum);
</script>

It's hard to say, due to you can console.log(nums)in console

What I want to point out is that, only developer know what is garbage we want to collect, the program can not tell in all cases.

 

The only thing program can consider it as garbage is the memory you can no longer access.

For example:

<script>
  let nums = [1, 2, 3, 4, 5];
  nums = [4,5,6,7]
  const sum = nums.reduce((total, num) => total + num, 0);

  console.log(sum);
</script>

Then program knows to cleanup [1, 2, 3, 4, 5]in memory, because there is no way to access this piece of data anymore.

 

What is memory leak?

For those piece of data, developer knows we don't need anymore, but still we are able to access those or we have references to it; then those will cause memory leak.

So the way to resolve this problem is make those piece of data unacessable again.

A simple example:

<script>
  let nums = [1, 2, 3, 4, 5];
  const sum = nums.reduce((total, num) => total + num, 0);

  console.log(sum);
  nums = null
</script>

In this end, we set nums = nullto tell GC to collect those memory.

 

Another example:

function createIncrease() {
  const doms = new Array(100000).fill(0).map((_, i) => {
    const dom = document.createElement('div');
    dom.innerHTML = i;
    return dom;
  });

  function increase() {
    doms.forEach((dom) => {
      dom.innerHTML = Number(dom.innerHTML) + 1;
    });
  }

  return increase;
}

const increase = createIncrease();
const btn = document.querySelector('button');
function handleClick() {
    increase()
    btn.removeEventListener('click', handleClick)
    increase = null
}
btn.addEventListener('click', handleClick);

We set increase = null, in this case, program can no longer access domsvariable. Then it should be collected by GC.

 

Memory leak in Closure

function createIncrease() {
  const doms = new Array(100000).fill(0).map((_, i) => {
    const dom = document.createElement('div');
    dom.innerHTML = i;
    return dom;
  });

  function increase() {
    doms.forEach((dom) => {
      dom.innerHTML = Number(dom.innerHTML) + 1;
    });
  }

  return increase;
}

const increase = createIncrease();
const btn = document.querySelector('button');
function handleClick() {
    increase()
    btn.removeEventListener('click', handleClick)
    increase = null
}
btn.addEventListener('click', handleClick);

This clousure example is hard to detect, normally we don't set increase = null. Due to increaseis just a function, how much memory it can takes anyhow?...

But for this function, it holds on a big chunk of data, then cause the memory leak.

 

Momory is not accessable but meanwhile cannot be GC collected?

This is possible, such as Last focused input/textarea element cause memory leak in Chrome

Another example:

function createIncrease() {
  const doms = new Array(100000).fill(0).map((_, i) => {
    const dom = document.createElement('div');
    dom.innerHTML = i;
    return dom;
  });

  function increase() {
  }
    
  function _temp() {
     doms
  }

  return increase;
}

let increase;
const btn = document.querySelector('button');
function handleClick() {
    increase = createIncrease()
}
btn.addEventListener('click', handleClick);

Now, increaseshas not access to doms, but a new function _tempwhich won't be called anyhow, but it holds on the reference to doms.

If we check the memory profile, we should see that the domswon't be GC collected.

 

Why?

Inside closure, if there is only one function increasewhich access doms, program will optimize syntactic environment to move away doms. In this case, it won't cause memory leak.

Inside closure, increase & _tempthey share the same syntactic environment, in this case, GC won't optimize the domsvariables, and also won't be able to garbage collect it and will cause memory leak.

当多个函数共享词法环境时,会导致词法环境膨胀,从而导致出现无法触达也无法回收的内存空间

 

posted @   Zhentiw  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
历史上的今天:
2022-11-04 [Typescript] Function scope in typescript
2020-11-04 [Javascript] Broadcaster + Operator + Listener pattern -- 15. Create a Sequence of Broadcasters
2020-11-04 [Kotlin Spring boot] Connect template with a backing bean
2020-11-04 [Kotlin Spring boot] Enable automatically recompile for Spring boot project with devtool
2020-11-04 [Tools MarkDown] Create a Diff in Markdown to Show What Has Changed in a Code Snippet
2020-11-04 [Javascript] Broadcaster + Operator + Listener pattern -- 14. Marking Done Based on a Condition
2019-11-04 [NPM + React] Prepare a Custom React Hook to be Published as an npm Package
点击右上角即可分享
微信分享提示