在Web浏览器中,内存泄漏可能不是大问题。但如果应用程序长时间运行,它可能会成为一个问题。本文将介绍如何在Web浏览器中检测内存泄漏。将使用示例来展示如何在开发工具中使用内存功能。注意到Chrome和Edge的开发工具是最易用的。
在Chrome或Edge开发工具中,可以获取三种类型的内存配置文件。
本文将专注于“堆快照”,它非常有效地帮助精确地定位哪个对象导致了内存泄漏。测试HTML页面如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<style>
button {
height: 60px;
border-radius: 5px;
font-weight: 800;
background-color: aquamarine;
}
</style>
<title>Memory test - first attempt</title>
<script>
let MTestClassesArray = [];
class MTestClass {
constructor() {}
}
const create_1000_objects = () => {
for (let i = 0; i < 1000; i++) {
MTestClassesArray.push(new MTestClass());
}
const text = `
Total ${MTestClassesArray.length} `
+
`
MTestClass objects created`;
document.getElementById('divMsg').innerHTML = text;
};
</script>
</head>
<body>
<button onclick="create_1000_objects()">Create 1000 MTestClass objects</button>
<div id="divMsg"></div>
</body>
</html>
在这个HTML页面中,有一个按钮。每次点击按钮,都会创建1000个"MTestClass"类型的对象。每个对象都被插入到数组"MTestClassesArray"中。由于数组"MTestClassesArray"是在全局作用域中声明的,因此这些对象不再可以被垃圾回收。
为了验证上述陈述,让加载页面并点击按钮几次。在实验中,使用了Edge。正如页面上所指示的,点击了按钮3次,创建了3000个"MTestClass"对象。可以通过"CTRL+SHIFT+i"调出开发工具。在获取内存快照之前,首先强制进行垃圾回收。
然后可以在垃圾回收后获取内存快照。内存快照为提供了对内存堆的极佳可见性。可以过滤快照以检查某些类类型的对象是否在内存堆中。在示例中,可以看到确切的3000个"MTestClass"对象。
还可以看到哪个对象阻止了"MTestClass"对象被垃圾回收。在示例中,它是"MTestClassesArray"对象。开发工具甚至可以告诉哪行代码创建了"MTestClass"对象。
一个现实世界的例子 - AsyncSubject 如果订阅了一个事件,需要取消订阅。如果不这样做,它很可能是一个内存泄漏。但对于rxjs中的"AsyncSubject"来说,情况是否如此?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<style>
button {
height: 60px;
border-radius: 5px;
font-weight: 800;
background-color: aquamarine;
}
</style>
<title>Memory test - async subject</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/7.1.0/rxjs.umd.js"></script>
<script>
const get_time = () => {
const element = document.getElementById('divTime');
const subject = new rxjs.AsyncSubject();
element.innerHTML = '';
setTimeout(() => {
subject.next(new Date());
subject.complete();
}, 3 * 1000);
subject.subscribe((data) => {
element.innerHTML = data.toString();
});
};
</script>
</head>
<body>
<button onclick="get_time()">Get Time by AsyncSubject</button>
<div id="divTime"></div>
</body>
</html>
在这个HTML页面中,有一个按钮。点击按钮后,当前时间将显示在页面上。没有直接获取时间,而是创建了一个"AsyncSubject",时间是在定时器回调函数中获取的。
然后程序“订阅”了"AsyncSubject"并在页面上显示它。需要注意的是,"AsyncSubject"是在"get_time"箭头函数中创建的,因此它不与任何全局上下文相关联。假设它在定时器回调后是可以被垃圾回收的。但让使用开发工具来确认这一点。
按钮点击和时间显示之间有3秒的延迟。为了确认"AsyncSubject"是可以被垃圾回收的,取两个堆快照。一个在定时器回调之前,一个在定时器回调之后。
在定时器回调之前,可以在内存堆中找到"AsyncSubject"。此时,它不能被垃圾回收,因为它被其他资源保留,并且最终可以从window对象访问。
但在定时器回调之后,"AsyncSubject"被收集了。如果没有被收集,可以手动强制收集。通过这个例子,可以对程序有更好的信心。如果"AsyncSubject"被正确使用,就不需要取消订阅事件。
附加说明 已经注意到,如果曾经"console.log()"一个对象,它就不再是可以被垃圾回收的。在生产发布中,如果有内存问题,应该记住这一点。
当在堆快照中搜索对象时,如果程序被压缩,类名可能会被压缩器更改。
这是一篇关于如何在Web浏览器中检测内存泄漏的笔记。 开发工具对来说非常有用,可以让对内存堆有一定的可见性。