递归 setTimeout 调用是否会导致闭包链内存泄漏?

Does a recursive setTimeout call cause a closure chain memory leak?

我有以下场景:

let func = () => {
  //...
  let id = setTimeout(() => {
    console.trace();
    clearTimeout(id);
    func();
  }, 2000);
}

func();

例如,如果我们在 Chrome 控制台中 运行 以下代码,我们将获得以下堆栈跟踪

console.trace
(anonymous) @ VM89644:4
setTimeout (async)
func @ VM89644:3
(anonymous) @ VM89644:6
setTimeout (async)
func @ VM89644:3
(anonymous) @ VM89644:6
setTimeout (async)
func @ VM89644:3
(anonymous) @ VM89644:6
setTimeout (async)
func @ VM89644:3
(anonymous) @ VM89644:6
setTimeout (async)
func @ VM89644:3
(anonymous) @ VM89644:6
setTimeout (async)
func @ VM89644:3
(anonymous) @ VM89644:6
...
...
...

每次迭代都在增加。最终它达到了一个最大值(我认为在 35 次迭代之后),链条 似乎 停止增长但我很好奇它是否只是 Chrome 只是没有显示它。到底发生了什么?

Does a recursive setTimeout call cause a closure chain memory leak?

没有。每次触发定时器后,浏览器中的定时器子系统将其 link 释放给定时器函数,定时器函数又将其 link 释放给创建它的环境,从而允许它们都被回收。

which keeps increasing on every iteration. Eventually it reaches a maximum point (i think after 35 iterations) where the chain seems to stop growing but I'm curious if it's simply Chrome just not displaying it. What exactly is happening?

这是标准的内存管理,根据 JavaScript 引擎的内存管理试探法进行清理,并不总是主动清理。我怀疑如果您反复点击 devtools 的 "Collect Garbage" 按钮(在“内存”选项卡上),您会注意到它返回到 near-baseline。

可能会阻止返回 near-baseline 的一件事是 V8 的漂亮 async stack traces。它们被宣传为 "zero cost," 但当然,这不能 完全 是真的,因为它们必须跟踪异步堆栈信息(即使只是一个字符串) .