我目前正在研究与我正在开发的应用程序中的 JavaScript 垃圾收集相关的内存使用模式。我构建了一个简单的基准测试来更好地理解 GC 的行为。它包括两个单独的任务(任务 A 和任务 B),如下所示。
const button = document.getElementById('begin-test');
button.onclick = () => {
console.log('start');
const LOOPS = 40000000;
let array = [];
// Task A
for (let index = 0; index < LOOPS; index++) {
const value = [{ value: index }];
array.push(value[0].value);
}
// Task B
for (let index = 0; index < LOOPS; index++) {
array.push(index);
}
array = null;
console.log('done');
}
}
任务 A 和任务 B 是分开执行的,因为我在测试期间交替评论其中之一。不再执行 Javascript。
应用程序启动时内存使用量约为 3MB。执行任务 A 后,Chrome 的内存检查工具显示内存使用量为 230MB。强制垃圾回收会将其恢复到 3MB。然而,执行任务 B 后,内存使用量报告为 580MB(!)。再次强制进行垃圾回收可将内存使用量减少至 3MB 左右。此行为在每个测试的新选项卡中都是一致的。
我对这种行为有三个疑问:
array = null
不会立即触发垃圾回收? (array = []
和 array.length = 0
都会产生相同的结果)GC 不会在取消引用资源后立即释放资源。不要依赖这样的假设
class HttpClient {
request(callId) {
return new Promise(async(resolve, reject) => {
try{
const response = await new Promise((resolve, reject) => {
if(Math.random() > .33) {
setTimeout(() => resolve(Math.random() > .5 ? {error:'session expired'} : {status: 'ok'}), 1000);
}else{
setTimeout(() => reject(new Error('some api error has happenned')), 1000);
}
});
if(response.error === 'session expired'){
console.log(callId + ': session expired, redirecting to login route');
return;
}
resolve(response);
}catch(e){
reject(e);
}
});
}
}
let prevMemory;
const http = new HttpClient;
const registry = new FinalizationRegistry(obj => {
console.log(obj, 'released');
});
let callId = 0;
async function test(){
const id = ++callId;
console.log('start ' + id);
prevMemory = performance.memory.usedJSHeapSize;
await new Promise(r => requestAnimationFrame(r));
const LOOPS = 40000000;
let array = [];
registry.register(array, `array ${id}`);
// Task A
for (let index = 0; index < LOOPS; index++) {
const value = [{ value: index }];
array.push(value[0].value);
}
// Task B
for (let index = 0; index < LOOPS; index++) {
array.push(index);
}
array = null;
console.log('done ' + id);
console.log('allocated bytes:', performance.memory.usedJSHeapSize - prevMemory);
prevMemory = performance.memory.usedJSHeapSize;
}
button{
padding: 5px 30px;
border-radius:5px;
box-shadow: 0 0 10px rgba(0,0,0,.2);
cursor:pointer;
}
<button onclick="test()">test</button>