用 Chrome 开发者工具分析 javascript 的内存回收(GC)

4,339 阅读3分钟

今天在知乎看到一个问题:"通过 createElment 创建的元素,不 append 到 html 中,那么此元素被自动销毁的时机是什么?"

比如:

var a;
(function(){
    a = document.createElment("div");
 })();
a = "Hello";

此时浏览器能否正确销毁和回收这个没有被 append 到页面上 dom 元素?

如果此元素不会被主动销毁或者回收,那么通过什么方法将其销毁呢?

我们知道,这种通过 document.createElment 创建出来的元素,是没有 parentNode 的,因为它创建时候并没有被添加到页面中。那么如何才能销毁它?


对 javascript 内存模型有了解的同学一定对下面的图很熟悉


当一个对象占用(使用)内存时,使用两种方式:直接和间接。直接占用内存很容易理解,而间接占用内存指对象中保持了对其它对象的引用,这样垃圾回收(GC 机制)就无法对那些对象进行回收。

在 GC 中一个很重要的概念就是 GC 根(GC root),当 javascript 程序中的某一个对象无法从 GC root 遍历到时,这个对象使用的内存就会被回收。

如下图的 9 和 10 就会被回收:


那么我们从原理角度分析一下最上面的代码,当执行 document.createElment("div") 时创建了一个 div 的 dom 对象,并赋值给了 a 变量,随后又把一个字符串 "Hello" 赋值给了 a 变量。此时 a 的值是 "Hello",类型是字符串。而之前创建的 div 对象已经不能通过 GC 遍历到,因此 div 对象被回收了。如果 div 对象没有被回收的话,我们观察如上代码,这个对象已经无法被任何变量所引用,因此就会产生内存泄漏。

下面我们通过使用 Chrome 的开发者工具分析一下如上代码。

首先新建一个空白 html 页面,页面里面什么代码也不写。

为了防止干扰,在 chrome 中新建一个隐身窗口,在隐身窗口中打开这个空白页面。

按 F12 或者 Ctrl + Shift + I,调出 devtools,选中 Profile 面板。


这时会记录此刻的内存使用情况


按 ESC 打开 Console,或者手动切换到 Console 面板,输入

var a;
(function(){
    a = document.createElment("div");
 })();

然后按 Ctrl + E,或者再次点击 Take Heap Snapshot 按钮,这时又会记录一个 Snapshot2,选择 Comparison,可以将本次内存和上次记录的进行对比。

差异比较多,好多可以不用管,看最后一个:


选中之后下面有详细:


Detached DOM 的意思是这个 DOM 是游离在页面 DOM 之外的。

运行代码

a = "Hello";

继续上面的步骤后,Snapshot3 和 Snapshot2 相比,DOM 被 Delete 了:


Snapshot3 和 Snapshot1 相比,多了字符串 "Hello":


而通过 Heap Allocation Timeline 可以观察到 DOM 到底在什么时候被 GC 的。


欢迎关注我的公众号,长按图片识别二维码