Chrome DevTools谷歌浏览器开发者工具如何录制堆快照

作者: Arvin Chen 分类: Chrome 来源: Break易站(www.breakyizhan.com)

学习如何用Chrome DevTools 堆分析器录制堆快照并查找内存泄漏。

Chrome DevTools 堆分析器显示页面 JavaScript 对象和相关 DOM 节点的内存分配 (请参见Objects retaining tree)。使用它来获取 JS 堆快照、 分析内存图、 比较快照,查找内存泄漏。

生成快照

Profiles(分析)面板中,选择 Take Heap Snapshot(生成堆快照),然后点击 Start(开始) 或者使用 Cmd + ECtrl + E 快捷键。

Snapshots(快照)最初存储在渲染内存中。当您根据需要点击快照图标来查看它的时候,他们会转移到DevTools中。

在快照加载到DevTools并且已经解析之后, 快照标题下方显示一个数字,该数字表示所有可访问的 JavaScript 对象的总大小:

清除快照

点击Clear all profiles(清除所有分析)图标可以清除快照(包括 DevTools 中和渲染内存中都会删除掉):

直接关闭 DevTools 窗口并不会删除渲染内存中的分析文件。当重新打开 DevTools 窗口的时候,所有之前生成的快照都会在快照列表中出现。

查看快照

不同的任务中使用不同角度查看快照:

Summary view(摘要视图) 按构造函数名称分组显示对象。用它来基于类型分组追捕对象 (和内存使用) 。它对追踪 DOM 泄漏特别有帮助。

Comparison view(比较视图) 显示两个快照之间的差异。用它来比较两个 (或更多) 内存快照的操作之前和之后。检查被释放的被引用的对象让您确认内存泄漏的原因。

Containment view(包含视图) 允许堆内容的探索。它提供更好的对象结构的视图,帮助分析全局命名空间 (window)周围是什么对象在引用。用它来分析闭包或对象的更深层次对象。

Dominators view(支配者视图) 显示[支配者树],并可用于发现积累(accumulation?)点。此视图可以帮助确认对象在删除/垃圾回收之前是否有外部引用任在工作。

视图之间进行切换,请使用视图底部的下拉框:

摘要视图(Summary view)

最开始的时候,快照是在总结视图中打开的,显示了对象的整体情况,并且该视图可以展开以显示实例信息:

顶级入口是 “total” 行,他们展示了:

  • Constructor(构造器),表示所有用这个构造器创建的对象。
  • 对象实例的数量 显示在 # 这一列下。
  • Shallow Size(浅尺寸) 这一列显示了当前构造器创建的所有对象的 Shallow Size(浅尺寸) 总和。Shallow Size(浅尺寸)是对象本身的内存的大小(一般,数组和字符串有较大的Shallow Size(浅尺寸))。另请参见Object sizes
  • Retained size(保留尺寸) 这一列显示相同的对象集所对应的最大 Retained size(保留尺寸)。删除对象 (依赖对象不再可达时) 可以释放的内存的大小被称为Retained size(保留尺寸)。另请参见Object sizes
  • Distance(距离) 显示了从根节点开始,从节点的最短路径到达当前节点的距离。

像上图那样展开 total line 之后,其所有的实例都会显示出来。对于每个实例,它的 shallow size 和 retained size 都会在相应列中展示出来。在 @ 字符后面的数字就是对象的 ID,该 ID 允许你在每个对象的基础上比较堆的快照。

请记住,黄色的对象表示有 JavaScript 对象引用了它们,而红色的对象是指从一个黄色背景节点引用的分离节点(detached nodes)。

在堆分析器中不同的Constructor(构造器)对应什么功能?

  • (global property)(全局属性) - 全局对象(如window)和它引用的对象之间的中间对象。如果使用构造函数Person创建对象并由全局对象持有,引用路径看起来像[global]>(global property)> Person。这跟一般的直接引用彼此的对象不一样。全局变量会定期修改,并且非全局变量的属性访问优化做得很好,但是不适用于全局变量。
  • (roots)(根) - 保留树视图中的根条目是具有对所选对象的引用的实例。它们也可以是由引擎为其自己的目的创建的引用。引擎具有引用对象的高速缓存,但是所有这样的引用都是弱引用,并且在没有强引用的情况下,不会阻止引用对象被回收。
  • (closure)(闭包) - 通过函数闭包引用的一组对象。
  • (array, string, number, regexp) - 对象类型的列表 , 这些对象的属性引用 Array,String,Number或正则表达式。
  • (compiled code)(编译代码) - 简单地说,一切都与编译代码有关。脚本与函数类似,但对应于<script>正文。SharedFunctionInfos(SFI)是位于函数和编译代码之间的对象。函数通常有一个上下文,而SFI没有。
  • HTMLDivElement,HTMLAnchorElement,DocumentFragment - 你代码中,一个特定引用类型对elements或document对象的引用。

比较视图

这个视图用于比较不同的快照,这样,你就可以通过比较它们的不同之处来找出出现内存泄露的对象。想要弄清楚一个特定的程序是否造成了泄露(比如,通常是相对的两个操作,就像是打开文档,然后关闭它,是不会留下内存垃圾的),你可以尝试下列步骤:

  1. 在执行操作前先生成一份快照。
  2. 执行操作(该操作涉及到你认为出现内存泄露的页面)。
  3. 执行一个相对的操作(做出相反的交互行为,并重复多次)。
  4. 生成第二份快照然后将视图切换到比较视图,将它与第一份快照对比。

在比较视图中,两份快照间的不同之处会展示出来。当展开一个总入口时,添加以及删除的对象实例会显示出来:

包含视图

包含视图本质上就像是你的应用程序对象结构的俯视图。它使你能够查看到函数闭包内部,甚至是观察到那些组成 JavaScript 对象的虚拟机内部对象,借助该视图,你可以了解到你的应用底层占用了多少内存。

这个视图提供了多个接入点:

  • DOMWindow objects - 这些是被认作“全局”对象的对象。
  • GC roots - 虚拟机垃圾回收器实际实用的垃圾回收根节点。
  • Native objects - 指的是“推送”到 JavaScript 虚拟机内以实现自动化的浏览器对象,比如,DOM 节点,CSS 规则。

关于闭包的小提示

为函数命名有助于你在快照中分辨不同的闭包。举个例子,下面这个函数没有命名:

function createLargeClosure() {
  var largeStr = new Array(1000000).join('x');

  var lC = function() { // this is NOT a named function
    return largeStr;
  };

  return lC;
}

而下面这个是命名后的函数:

&amp;amp;amp;lt;pre&amp;amp;amp;gt;function createLargeClosure() {
  var largeStr = new Array(1000000).join('x');

  var lC = function lC() { // this IS a named function
    return largeStr;
  };

  return lC;
}

支配者视图

Dominators(支配者)视图显示了堆图的支配树,从形式上来看,支配者视图有点像是包含视图,但是缺少了某些属性。这是因为支配者对象可能会缺少对它的直接引用,也就是说,支配树不是生成树。

示例:尝试一下这个例子来分析闭包对内存的影响。你可能会对下面这个例子感兴趣,它可以让你深入了解堆内存分配

查看代码颜色

对象的属性以及属性值属于不同类型并且有着相应的颜色。每个属性都会有四种类型之一:

  • a:property - 有名称的常规属性,通过 .(点)操作符或者 [](方括号)符号来访问,例如 ["foo bar"];
  • 0:element - 有数字下标的常规属性,使用 [](方括号)来访问。
  • a:context var - 函数上下文中的某个变量,在相应的函数闭包中使用其名字就可以访问。
  • a:system prop - 由 JavaScript 虚拟机添加的属性,在 JavaScript 代码中无法访问。

被命名为 System 这样的对象是没有相应的 JavaScript 类型的。他们是 JavaScript 虚拟机的对象系统的一部分。V8 将大多数内部对象分配到和用户 JS 对象相同的堆中,所以这些都只是 V8 内部内容。

找到特定对象

要在堆中找到某个对象,你可以使用 Ctrl + F 来打开搜索框,然后输入对象的 ID。

发现 DOM 内存泄露

该工具的一大特点就是它能够显示浏览器本地对象(DOM 结点,CSS 规则)以及 JavaScript 对象间的双向依赖关系。这有助于发现因为忘记分离 DOM 子树而导致的不可见的泄露。

DOM 泄露肯能比你想象中的要多。考虑下面这个例子 - 什么时候 #tree 会被回收?

var select = document.querySelector;
var treeRef = select(&amp;amp;quot;#tree&amp;amp;quot;);
var leafRef = select(&amp;amp;quot;#leaf&amp;amp;quot;);
var body = select(&amp;amp;quot;body&amp;amp;quot;);

body.removeChild(treeRef);

//#tree can't be GC yet due to treeRef
treeRef = null;

//#tree can't be GC yet due to indirect
//reference from leafRef

leafRef = null;
//#NOW can be #tree GC

#leaf 包含了对其父亲(父节点)的引用并递归到 #tree,所以只有当 leafRef 失效的时候 #tree 下的整棵树才能被回收。

想要了解更多关于 DOM 泄露以及内存分析的基础内容,请参阅 Gonzalo Ruiz de Villa 编写的Finding and debugging memory leaks with the Chrome DevTools

说明

翻译自:https://developers.google.com/web/tools/chrome-devtools/console/events

 

 

本文内容来自:Chrome DevTools谷歌浏览器开发者工具如何录制堆快照– Break易站


—Author: Arvin Chen —Web Address: www.breakyizhan.com (Break易站)

  •   本文标题:Chrome DevTools谷歌浏览器开发者工具如何录制堆快照 - Break易站
    转载请保留页面地址:https://www.breakyizhan.com/chromeconsole/2259.html
    扫描二维码添加微信 
  • ,领取淘宝优惠券,淘宝购物更优惠。现在添加微信,还可以领取机械键盘优惠券!添加微信后,分享淘宝选中的机械键盘给淘宝机器人即可领取!
    支持我们,就用微信淘宝!

    发表笔记

    电子邮件地址不会被公开。 必填项已用*标注

    更多阅读