Web浏览器内存泄漏检测指南

在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浏览器中检测内存泄漏的笔记。 开发工具对来说非常有用,可以让对内存堆有一定的可见性。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485