[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 doms
variable 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 = null
to 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 doms
variable. 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 increase
is 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, increases
has not access to doms
, but a new function _temp
which won't be called anyhow, but it holds on the reference to doms
.
If we check the memory profile, we should see that the doms
won't be GC collected.
Why?
Inside closure, if there is only one function increase
which access doms
, program will optimize syntactic environment to move away doms
. In this case, it won't cause memory leak.
Inside closure, increase & _temp
they share the same syntactic environment, in this case, GC won't optimize the doms
variables, and also won't be able to garbage collect it and will cause memory leak.
当多个函数共享词法环境时,会导致词法环境膨胀,从而导致出现无法触达也无法回收的内存空间
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源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