「这是我参与11月更文挑战的第12天,活动详情查看:2021最后一次更文挑战」
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
性能更佳。