浅谈JavaScript 中释放内存(js中哪些操作会造成内存泄露)
JavaScript 中释放内存的方式
JavaScript 采用引用计数的方式来动态管理内存。
什么是引用计数呢?举例如下:
// 创建了一个 obj 对象,动态分配了一些内存 【obj 引用计数为 0】 let obj = { value: 'XXXXX' }; // data1 引用了 obj,【obj 引用计数变为 1】 let data1 = { value: obj } // data2 引用了 obj,【obj 引用计数变为 2】 let data2 = { value: obj } // data1 释放对 obj 的引用,【obj 引用计数变为 1】 data1.value = undefined; // data2 释放对 obj 的引用,【obj 引用计数变为 0】 delete data2.value; // 假设后续代码再无对 obj 的引用 // 此时 obj 引用计数为 0,当 GC 发生时,obj 的内存将会被回收复制代码
简单来说,JavaScript 引擎在运行时统计一块内存被引用的次数,并定期进行垃圾回收(GC
)。如果一块堆内存的引用次数变为 0,则会在后续的 GC
中被回收,也就是我们常说的这块内存被释放了。
所以当一个 JavaScript 中的变量确定不会再被使用之后,我们可以手动解除对它的引用,以便能够释放内存。通常,解除引用的方式有几种:
设为 undefined,例如
data.value = undefined
设为 null,例如
data.value = null
delete,例如
delete data.value
它们的性能差异如何呢?
实验过程
delete
先来看看 delete
的性能表现。
正向 delete
(()=>{ let totalTime = 0, N=10; for(let n=0; n<N; ++n){ let a = {}; for(let i=0; i<1000000; ++i){ a[i] = i; } let startTime = Date.now(); for(let i=0; i<1000000; ++i){ delete a[i]; } totalTime += Date.now() - startTime; a; } console.log(`avg ${totalTime/N}ms in ${N} times`); })()复制代码
运行结果
avg 67.5ms in 10 times复制代码
反向 delete
(()=>{ let totalTime = 0, N=10; for(let n=0; n<N; ++n){ let a = {}; for(let i=0; i<1000000; ++i){ a[i] = i; } let startTime = Date.now(); for(let i=1000000-1; i>-1; --i){ delete a[i]; } totalTime += Date.now() - startTime; a; } console.log(`avg ${totalTime/N}ms in ${N} times`); })()复制代码
结果
avg 64.4ms in 10 times复制代码
设为 undefined
(()=>{ let totalTime = 0, N=10; for(let n=0; n<N; ++n){ let a = {}; for(let i=0; i<1000000; ++i){ a[i] = i; } let startTime = Date.now(); for(let i=0; i<1000000; ++i){ a[i] = undefined; } totalTime += Date.now() - startTime; a; } console.log(`avg ${totalTime/N}ms in ${N} times`); })()复制代码
运行结果
avg 0.8ms in 10 times复制代码
设为 null
(()=>{ let totalTime = 0, N=10; for(let n=0; n<N; ++n){ let a = {}; for(let i=0; i<1000000; ++i){ a[i] = i; } let startTime = Date.now(); for(let i=0; i<1000000; ++i){ a[i] = null; } totalTime += Date.now() - startTime; a; } console.log(`avg ${totalTime/N}ms in ${N} times`); })()复制代码
运行结果
avg 0.8ms in 10 times复制代码
结论
通过设为
undefined
或null
来解引用,比delete
快近 70 倍没有看出明显的内存差异。
所以,在性能敏感的场景解引用,通过赋值为 undefined
或 null
性能更佳。
作者:nelyo
链接:https://juejin.cn/post/7031091616957857829