Chrome 控制台使用手册专栏【九】Memory Panel

6,127 阅读10分钟

Memory Panel 可以查看当前网页的内存分配情况,用来排查内存泄漏问题

JS内存分析

内存泄露是指计算机内存逐渐丢失。当某个程序总是无法释放内存时,就会出现内存泄露。JavaScript web 应用程序可能会经常遇到类似于本地程序中内存泄露这样的问题,比如泄露和膨胀,但是 JavaScript 有内存回收机制可以解决此类问题。

最简单检查内存情况就是使用Google自带的任务管理器,快速查看页面的内存使用情况

打开任务管理器(使用快捷键 Shift + Escimage.png

image.png

允许查看任务其他信息 image.png

需要查看页面跟详细的内存使用情况,打开 Memory Panel 面板查看

打开Memory

打开控制台,切换Memory Panel image.png

使用命令 Show Memory 打卡Memory Panel image.png

布局

image.png

操作栏:执行快照录制、删除 image.png

快照列表 image.png

选择快照类型(模式) image.png

1)Heap snapshot:堆快照

2)Allocation instrumentation on timeline:分时段的内存占用

3)Allocation sampling:分配抽样

Heap snapshot:堆快照

选择 Heap snapshot,点击image.png image.png

生成一个快照,包括快照名称以及快照大小,点击选中快照,查看当前页面各种对象的大小 image.png

右击快照,支持 重新加载、删除、保存操作 image.png

快照右侧也支持保存操作 image.png

快照详情,支持多种角度查看 image.png

  • Summary view 摘要视图 显示按构造函数名称分组的对象。使用它根据按构造函数名称分组的类型来搜索对象(及其内存使用)。它对于追踪 DOM 泄漏特别有帮助。

  • Comparison view 比较视图 显示两个快照之间的差异。使用它来比较操作前后的两个(或多个)内存快照。检查释放内存和引用计数中的增量可让您确认内存泄漏的存在和原因。

需要两个 heap snapshot 才会展示 image.png

  • Containment view 包含视图 允许查看堆内容。它提供了一个更好的对象结构视图,帮助分析在全局命名空间(窗口)中引用的对象,以找出它们周围的原因。使用它来分析闭包并深入了解您的对象。

  • Dominators view 支配者视图 显示支配者树,可用于查找累积点。此视图有助于确认没有对对象的意外引用仍然存在,并且删除/垃圾收集实际上正在工作。

  • Statistics view 统计视图 显示内存大小使用统计(扇形图)

Summary view 摘要视图

切换角度,选择Summary,按构造函数名称分组展示快照 image.png

允许展开分组查看构造函数,选择构造函数查看构建的对象,同时允许查看对象的声明位置以及大小 image.png

堆照信息 image.png

  • Constructor 构造函数 表示使用此构造函数创建的所有对象。
  • Number of object instances 对象实例的数量显示在# 列中。
  • Shallow size 浅尺寸 列显示由某个构造函数创建的所有对象的浅尺寸总和。浅层大小是对象本身持有的内存大小(通常,数组和字符串具有较大的浅层大小)。另请参阅对象大小
  • Retained size保留大小 列显示同一组对象中的最大保留大小。一旦对象被删除(并且其依赖项不再可访问)可以释放的内存大小称为保留大小。另请参阅对象大小
  • Distance 距离 使用最短的简单节点路径显示到根的距离。

分组条目 image.png

  • (global)  ——全局对象(如“窗口”)和它引用的对象之间的中间对象。如果一个对象是使用构造函数 Person 创建的并且由一个全局对象持有,则保留路径看起来像 [global] > (global property) > Person。这与对象直接相互引用的规范形成对比。出于性能原因,我们有中间对象。全局对象会定期修改,属性访问优化对非全局对象做得很好,但不适用于全局对象。
  • (roots)  – 保留树视图中的根条目是具有对选定对象的引用的实体。这些也可以是引擎为其自身目的创建的引用。引擎缓存了哪些引用对象,但所有这些引用都是弱引用,并且不会阻止对象被收集,因为没有真正的强引用。
  • (closure)  – 通过函数闭包对一组对象的引用计数
  • (array, string, number, regexp)  – 对象类型列表,具有引用数组、字符串、数字或正则表达式的属性。
  • (compiled code)  ——简单地说,与编译代码相关的一切。脚本类似于函数,但对应于<script>主体。SharedFunctionInfos (SFI) 是位于函数和编译代码之间的对象。函数通常有上下文,而 SFI 没有。
  • HTMLDivElementHTMLAnchorElementDocumentFragment等——对代码引用的特定类型的元素或文档对象的引用。

构造函数过滤、支持名称过滤,以及快照过滤 image.png

Comparison view 比较视图

通过相互比较多个快照来查找泄漏的对象。要验证某个应用程序操作是否不会造成泄漏(例如,通常一对正向和反向操作,例如打开文档,然后关闭它,不应留下任何垃圾)

切换角度,选择Comparison,同时可以从右边快照列表选择对比的快照 image.png

展开总条目时,会显示添加和删除的对象实例 image.png

演示页面

Containment view 包含视图

包含视图本质上是应用程序对象结构的“鸟瞰图”。它允许您查看函数闭包内部,观察共同构成 JavaScript 对象的 VM 内部对象,并在非常低的级别了解您的应用程序使用了多少内存。

切换角度,选择Containment image.png

  • DOMWindow objects 是被视为 JavaScript 代码的“全局”对象的对象。
  • GC roots 是 VM 垃圾使用的实际 GC 根。GC 根可以由内置对象映射、符号表、VM 线程堆栈、编译缓存、句柄范围、全局句柄组成。
  • Native objects原生对象是在 JavaScript 虚拟机中“推送”以允许自动化的浏览器对象,例如 DOM 节点、CSS 规则。

演示页面

例子: 试试这个为什么 eval 是邪恶的例子来分析闭包对内存的影响。您可能也有兴趣跟进这个示例,该示例将引导您完成记录堆分配

示例

声明一个 person_memory 对象实例,在DOMWindow objects中找到该对象 image.png

新增birthday字段,重新生成快照 image.png

指定 person_memory = null 回收对象 image.png

Dominators view 支配者视图

显示了堆图的支配树。它看起来类似于 Containment 视图,但缺少属性名称。这是因为对象的支配者可能缺少对它的直接引用;支配树不是图的生成树。但这只是好事,因为它可以帮助我们快速识别内存积累点。

image.png

Statistics view 统计视图

切换角度,选择Statistics,快照展示为扇形统计图 image.png

查找特定对象

要在收集的堆中查找对象,您可以使用并提供对象 ID 进行搜索。Ctrl + F

Allocation instrumentation on timeline:分时段的内存占用

选择 Allocation instrumentation on timeline,点击image.png image.png

在点击image.png,生成时段快照 image.png

多了timeline时间线,支持查看不同时间段的内容占用情况 image.png

每个条的高度对应于最近分配的对象的大小,条的颜色表示这些对象是否仍然存在于最终的堆快照中。蓝色条表示在时间线结束时仍然存在的对象,灰色条表示在时间线期间分配但已被垃圾回收的对象

快照角度缺少对比快照、但是多了 Allocation View 堆栈分配跟踪,需要勾选复选框Record stack trances of allocations image.png

image.png

image.png

Allocation sampling:分配抽样

使用采样方法记录内存分配。此配置文件类型具有最小的性能开销 并可用于长时间运行的操作。它通过 JavaScript 执行堆栈提供了分解分配的良好近似值

选择 Allocation sampling,点击image.png image.png

在点击image.png,生成分配抽样快照 image.png

术语

Object sizes 对象大小

一个对象可以通过两种方式保存内存:

  • 直接由对象本身。
  • 隐含地持有对其他对象的引用,从而防止这些对象被垃圾收集器(简称GC)自动处理。

堆分析器中,展示Shallow SizeRetained Size信息描述对象的大小

Shallow size 浅尺寸

这是对象本身持有的内存大小。

典型的 JavaScript 对象为它们的描述和存储立即值保留了一些内存。通常,只有数组和字符串可以具有明显的浅大小。但是,字符串和外部数组通常将它们的主要大小存储在渲染器(renderer)内存中,只在 JavaScript 堆上暴露一个小的包装器对象。

渲染器(renderer)内存是渲染页面的进程的所有内存:本机内存 + 页面的 JS 堆内存 + 由页面启动的所有专用工作人员的 JS 堆内存。尽管如此,即使是一个小对象也可以通过防止其他对象被自动垃圾收集过程丢弃而间接持有大量内存。

Retained size 保留尺寸

这是在删除对象本身及其从GC roots无法访问的依赖对象后释放的内存大小。

GC roots在从本机代码引用 V8 之外的 JavaScript 对象时创建(本地或全局)的句柄组成。 所有这些句柄都可以在GC roots>Handle scopeGC roots>Global handles下的堆快照中找到。

有很多内部 GC roots,其中大部分对用户来说并不需要感兴趣。从应用程序的角度来看,有以下几种根:

  • Window global object (窗口全局对象)(在每个 iframe 中)。堆快照中有一个距离字段,它是距离窗口的最短保留路径上的属性引用数。
  • Document DOM tree(文档 DOM 树)由遍历文档可访问的所有本机 DOM 节点组成。并非所有人都可能具有 JS 包装器,但如果它们具有包装器,则在文档处于活动状态时它们将处于活动状态。
  • 有时对象可能会被console context(调试器上下文)和 DevTools 控制台保留(例如在控制台赋值之后)。使用干净的控制台创建堆快照,并且在调试器中没有设置断点。

内存图以根开始,它可能是window浏览器的Global对象或 Node.js 模块的对象。你不能控制这个根对象是如何被 GC 的。

无法控制根对象

任何从根无法访问的东西都会得到 GC。

Objects retaining tree 对象保留树

堆是互连对象的网络。在数学世界中,这种结构称为内存图。一个图是由通过边连接的 节点构成的,边都被给定了标签。

  • 节点或对象)使用用于构建它们的构造函数的名称进行标记。
  • 边缘使用属性的名称进行标记。

Dominators 支配者

Dominator对象由树结构组成,因为每个对象都只有一个Dominator。一个对象的Dominator可能缺乏对其支配的对象的直接引用。也就是说,支配者的树不是内存图的生成树。

  • 节点 1 支配节点 2
  • 节点 2 支配节点 3、4 和 6
  • 节点 3 支配节点 5
  • 节点 5 支配节点 8
  • 节点 6 支配节点 7

支配树结构

日常使用

传送门

检查DOM 树内存泄漏

检查 JS 堆内存泄漏

检查函数调用占用内存大小,优化代码

使用Allocation sampling 类型查看 JavaScript 函数的内存分配,在录制快照时执行查看的函数。

查看垃圾回收情况,优化代码

使用Allocation instrumentation on timeline 类型,在快照录制时执行函数,执行完毕后结束快照,查看期间垃圾回收情况。

在任务管理器(Chrome Task Manager)中,频繁上升和下降的MemoryJavaScript Memory值表示频繁的垃圾回收。在 Timeline Memory记录中,频繁上升和下降的 JS 堆或节点计数图表示频繁的垃圾收集。


参考