JavaScript中该闭包场景引发内存泄漏的原因是什么?
闭包作用域变量内存留存原因说明
测试代码如下:
function outer() { let a return function inner() { a = new Uint8Array(100000) const b = new Uint16Array(100000) }; }; const fn = outer(); fn()
你观测到的Uint8Array在堆快照中留存不属于内存泄漏,是Chrome使用的V8引擎对闭包上下文的正常处理逻辑,核心规则如下:
- 当外层函数
outer执行完毕返回内部函数inner时,V8会为这个被返回的闭包创建专属的上下文存储结构,用来保存所有被闭包引用的外层作用域变量绑定。只要闭包本身(也就是代码里的全局变量fn)还处于可达状态,这个上下文就不会被销毁。 - 变量
b是inner函数的内部局部变量,每次inner执行结束后,没有任何持有关系能指向b绑定的Uint16Array实例,因此会被垃圾回收器直接回收,和你观测到的第二次快照无对应实例的现象一致。 - 变量
a定义在outer作用域,且被inner函数引用,属于闭包上下文必须保留的变量绑定。你第一次执行fn()时给a赋值的Uint8Array实例,会一直被闭包上下文持有引用,哪怕你无法在outer外部直接读写a的值,这条隐式的引用链路也一直存在。
垃圾回收的判断标准是从GC根对象出发是否存在可达的引用路径,和开发者能不能在业务代码里手动访问到某个值没有必然关系。这里的引用链路是:全局对象(GC根)→ 全局变量
fn→fn关联的闭包上下文 → 变量a→Uint8Array实例,整条链路完全可达,实例自然不会被回收。
你可以自行验证:执行完fn()后将全局变量fn赋值为null,切断闭包的可达引用,之后再采集堆快照就会发现之前留存的Uint8Array实例已经被回收,符合正常的垃圾回收逻辑。真正的内存泄漏是指开发者已经完全不需要使用的内存被意外持有、即使释放相关引用也无法回收的场景,你遇到的现象不属于这类问题。
内容的提问来源于stack exchange,提问作者Joji
相关产品推荐
相关产品推荐

