一.前言
在当前的 Chrome 中,追踪内存泄漏较为容易。
Chrome DevTools 的Memory功能 可以对C++DOM对象 进行追踪和快照,并显示 JS 中所有可访问的 DOM对象及其引用。
当未使用对象由于其他对象的引用未释放时,就发生了垃圾回收系统中的内存泄漏。
前端的内存泄漏通常涉及 JS对象 和 DOM元素 的交互。
如下代码。开发者忘记移除事件监听器而导致内存泄漏。事件监听器引用的所有对象都不能被垃圾回收,同时 iframe 和 事件监听器 一起泄漏。并且泄漏的 iframe 使得它的所有对象均泄漏。
// Main window:
const iframe = document.createElement('iframe');
iframe.src = 'iframe.html';
document.body.appendChild(iframe);
iframe.addEventListener('load', function() {
const localVariable = iframe.contentWindow;
function leakingListener() {
// Do something with `localVariable`.
if (localVariable) {}
document.body.addEventListener('my-debug-event', leakingListener);
document.body.removeChild(iframe);
// BUG: forgot to unregister `leakingListener`.
});
// iframe.html:
class Leak {};
window.globalVariable = new Leak();
学习保留路径的概念有助于找到内存泄漏。
保留路径是一条对象链,每个对象都是阻止垃圾回收的泄漏对象。它从一个根对象开始,通常是window全局对象,在泄漏对象处结束。
链中的每个中间对象,都有对链中下一个对象的直接引用。
例如,iframe 中 Leak对象 的保留路径如下:
保留路径两次跨越 JavaScript / DOM 边界(分别以绿色/红色显示)。
JavaScript 对象存在于 V8 堆中,而 DOM 对象是 Chrome 中的 C++ 对象。
你可以访问一个线上实例,去查看堆快照中的内存泄漏 线上 iframe 内存泄漏
二.DevTools堆快照
可通过在 DevTools 中堆快照来检查任何对象的保留路径。堆快照精确地捕获了 V8 堆上的所有对象。
DevTools 通过 C++ DOM 对象进行跟踪,并精确地捕获 JS对象 和 C++ DOM 之间的引用。
第一行说明:该Leak对象存储于 iframe 中 window 对象的 global_variable 。
三.底层:跨组件追踪
DOM 对象由 Blink 管理。Blink是Chrome 的渲染引擎,它负责将 DOM 转换为屏幕上的实际文本和图像。
Blink 和它对 DOM 的表示是用 C++ 编写的,这意味着 DOM 不能直接暴露给 JS。
DOM 中的对象分为两部分:JS 可用的 V8 包装器对象和表示 DOM 中节点的 C++ 对象。这些对象之间彼此引用。
跨组件追踪是一种通过从 JS 跟踪到 C++ 实现的DOM ,并返回来确定对象活跃度的机制。