[转载] Closure 与 GC
本帖最后由 cyfung1031 于 2026-2-5 20:47 编辑https://x.com/jarredsumner/status/2017825694731145388
!(data/attachment/forum/202602/05/190046b653rj9k03g2jc2j.png)
今天 Bun 的作者 Jarred Sumner 在推特上发了个文,说对于跑很久的 Claude code session 来说,底下的改动可以省掉 1GB 的记忆体:
Before:() => controller.abort()
After:controller.abort.bind(controller)
从原本的 function 换成了 bind 的用法,为什么这样就可以呢?或是换个方式问,为什么原本的写法会吃比较多记忆体?
在原文附图的注解里就有写原因了,说原本用 closure 的写法,会把整个 scope 记住,而这个 scope 有 request body 跟其他大的物件,所以在结束以前,这些被包住的东西都没办法被 GC,就会一直占空间。
而修改后的做法没有 closure 了,所以不会记住那些无关的东西。
这个问题其实在我的书《JavaScript 重修就好》里面就有提过了,有书的同学可以打开翻到 4-44,讲 closure 可能造成的潜在问题,我给了这样的案例:
```
function createWebSocketHandler() {
let socket = new WebSocket("wss://example.com/chat");
let messages = [];
socket.onmessage = function(event) {
messages.push(event.data);
};
return {
sendMessage: function(text) {
socket.send(text);
},
closeConnection: function() {
socket.close();
socket.onmessage = null;
socket = null;
}
};
}
const chatHandler = createWebSocketHandler();
chatHandler.sendMessage("Hello, world!");
chatHandler.closeConnection();
```
在关闭连线时,我们将 socket close,然后把有用到 messages 的 onmessage 清掉,也把 socket 整个清掉,看起来没人用到 messages 了,就想说安全,既然没人用到那就可以被 GC 了。
但这是在使用 closure 时会产生的错觉,那就是「只有我有用到的东西才会被记住」。事实上,closure 才没有在管你使用与否,只要是同一个 scope 的东西就全部记了下来。
因此,尽管 sendMessage 与 closeConnection 这两个函式没有用到 messages, 它依然被引用了。所以就算把 socket.onmessage 给清除,messages 的记忆体空间还是没办法被回收。
重点只有一个,就是 closure 是整个 scope 都会记著,被记住的东西就不会被 GC 了。这恰巧也是 Claude code 碰到的问题,没有察觉到那个被回传的 function 会记住整个 scope。
然后这也跟 AI 写 code 一点关系都没有,一堆人类也会写出这样的 code,况且现在人跟 AI 谁写得比较好还很难说呢,尤其是对那些非工程师来说。
这个案例是人是 AI 都有可能犯错,但如果是其他案例,当有人责怪 AI 怎么写出这种烂 code 的时候,AI 说不定会想跳出来抱怨:「我才不会犯这种错呢,这一定是人类写的」。
页:
[1]