本文介绍内存分析中使用的常见术语,适用于不同语言的各种内存分析工具。

此处所述的术语和概念是指 “内存”面板 。 如果曾经使用过 Java、.NET 或其他内存探查器,则本文可能是一个刷新程序。

将内存视为具有基元类型 (如数字和字符串) 和对象 (关联数组) 的图形。 内存可以直观地表示为具有多个互连点的图形,如下所示:

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

  • 直接;内存由对象本身持有。

  • 隐式,通过保留对其他对象的引用。 包含对其他对象的引用的对象可防止垃圾回收器 (GC) 自动释放这些对象。

    DevTools 中的 “内存 ”面板是用于调查内存问题的工具。

    使用“内存”面板时,你可能会发现自己查看了几个不同的信息列。 突出的两列是 “浅层大小” “保留大小 ”:

    浅层大小 是对象持有的内存大小。

    典型的 JavaScript 对象保留一些内存,用于说明和存储即时值。 通常,只有数组和字符串可以具有很大的浅层大小。 但是,字符串和外部数组通常在呈现器内存中具有其主存储,在 JavaScript 堆上仅公开一个小包装器对象。

    呈现器内存 是呈现已检查页的进程的所有内存:

    呈现器内存 = 本机内存 + 页面 + 的 JS 堆内存 页面启动的所有专用辅助角色的 JS 堆内存

    但是,即使小型对象也可以间接保存大量内存,因为自动垃圾回收过程会阻止其他对象被释放。

    保留的大小

    保留的大小 是删除对象后释放的内存大小,以及从垃圾回收根目录 (GC 根) 无法访问的依赖对象。

    垃圾回收根 句柄 组成,在从本机代码引用 V8 VM 外部的 JavaScript 对象时, (创建为本地或全局) 。 所有这些句柄都可以在“ GC 根根 句柄范围 ”和 “GC 根 >> 全局句柄 ”下的堆快照中找到。 在不深入了解浏览器实现的详细信息的情况下描述本文档中的句柄可能会令人困惑。 垃圾回收根和句柄都不需要担心。

    有许多内部 GC 根,其中大多数对用户来说并不感兴趣。 从应用程序的角度来看,有以下类型的根:

  • 每个 iframe) 中的窗口全局对象 (。 在堆快照中, distance 字段指示窗口最短保留路径上的属性引用数。

  • 文档 DOM 树,由可通过遍历文档访问的所有本机 DOM 节点组成。 并非所有节点都有 JavaScript 包装器,但如果节点具有包装器,则节点在文档处于活动状态时处于活动状态。

  • 有时,对象由 工具和 控制台 中的调试上下文保留,例如在控制台评估之后。 使用清除的 控制台 工具创建堆快照,并且 工具的调试器中没有活动断点。

    内存 工具中创建堆快照之前,请清除 控制台 工具并在 “源” 工具中停用断点。 若要清除 控制台 工具,请 clear() 运行 方法。

    内存图以根开头,根可能是 window 浏览器的对象或 Global Node.js模块的对象。 你无法控制如何对根对象进行垃圾回收。

    任何无法从根目录访问的东西都会被垃圾回收。

    浅表大小 保留大小 列中显示的数字是字节数。

    对象保留树

    堆是互连对象的网络。 在数学世界中,此结构称为 图形 内存图 。 图形是从通过边缘连接的 节点 构造

    图形中的节点和边缘具有如下标签:

  • ( 对象 ) 的节点使用用于生成它们的 构造函数 的名称进行标记。

  • 边缘 使用 属性 名称进行标记。

    了解如何使用堆探查器记录配置文件 。 在下图中, 内存 工具的堆快照记录中的一些值得注意的内容包括 距离 ,这是与垃圾回收根目录的距离。 如果同一类型的几乎所有对象都位于同一距离,并且一些对象位于更大的距离上,则值得研究。

    与根的距离:

    控制器对象由树结构组成,因为每个对象只具有一个控制器。 对象的主导器可能缺少对它主导的对象的直接引用。 也就是说,控制器的树不是图形的跨越树。

    下图所示:

  • 节点 1 主导节点 2。
  • 节点 2 主导节点 3、4 和节点 6。
  • 节点 3 主导节点 5。
  • 节点 5 主导节点 8。
  • 节点 6 主导节点 7。
  • 在下图中, 节点 #3 是节点 #10 的主导器。 但节点 #7 也存在于从垃圾回收根 GC 到节点 #10 的每个简单路径中。 因此,如果 对象 B 存在于从根到 对象的 A 每个简单路径中,则对象 B 是对象的 A 主导器。

    节点 GC 主导节点 #1 #3 #11

    节点 #3 由节点 GC 主导,并主宰节点 #7

    节点 #7 由节点 #3 主导,并主导节点 #8 #9 #10

    节点 #8 由节点 #7 主导,不主导任何节点:

    节点 #10 由节点 #7 主导,不主导任何节点:

    节点 #11 由节点 #1 主导,不主导任何节点:

    V8 细节

    分析内存时,了解堆快照为何以某种方式显示会很有帮助。 本部分介绍一些与内存相关的主题,这些主题专门对应于 V8 JavaScript 虚拟机 (此处缩写为 V8 VM 或仅 VM ) 。

    JavaScript 对象表示形式

    在 JavaScript 中,有三种基元类型:

  • 数字 (,例如 3.14159... ) 。
  • 布尔值 ( true false ) 。
  • 字符串 (,例如 "Werner Heisenberg" ) 。
  • 基元不能引用其他值,并且始终是叶节点 (也称为 终止节点 ) 。

    数字 可以存储为:

  • 称为小整数的即时 31 位 整数 值 ( SMIs ) 。

  • 堆对象,称为 堆编号 。 堆编号用于存储不适合 SMI 形式的值(例如 双精度 型),或者当值需要 装箱 时(例如设置其属性)。

    字符串 可以存储在以下任一中:

  • VM 堆

  • 呈现器内存 的外部。 将创建 包装对象 并将其用于访问外部存储,例如,从 Web 接收的脚本源和其他内容被存储,而不是复制到 VM 堆。

    新 JavaScript 对象的内存是从专用 JavaScript 堆 (或 VM 堆 ) 分配的。 这些对象由 VM V8 的垃圾回收器管理,因此,只要至少有一个对它们的强引用 ,这些对象就保持活动状态。

    本机对象 - JavaScript 堆中没有的任何 内容称为本机对象 。 与堆对象相比,本机对象在其整个生命周期内不受 V8 垃圾回收器管理,并且只能通过使用 JavaScript 包装器对象从 JavaScript 访问。

    串联字符串 (串联字符串) 是一个对象,它由存储并随后联接的字符串对组成,是串联的结果。 cons 字符串 内容的联接仅根据需要进行。 例如,需要构造联接字符串的子字符串时。

    例如,如果连接 a b ,则会收到一个表示串联结果的字符串 (a, b) ,并且是 cons 字符串。 如果稍后与该结果连接 d ,则会收到另一个 cons 字符串: ((a, b, d)

    数组 - 数组 是具有数字键的对象。 数组在 V8 VM 中广泛使用,用于存储大量数据。 用作字典的键值对集作为 数组 实现。

    典型的 JavaScript 对象仅存储为以下两种 数组 类型之一:

    典型的 JavaScript 对象可以是以下两种数组类型之一:

  • 用于存储命名属性的数组。
  • 用于存储数值元素的数组。
  • 当存在少量属性时,这些属性存储在 JavaScript 对象内部。

    Map 是一个对象,用于描述它所作为对象的种类和布局。 例如,映射用于描述 用于快速访问属性 的隐式对象层次结构。

    每个 本机对象组 由保存相互引用的对象组成。 例如,考虑一个 DOM 子树,其中每个节点都有一个指向相对父级的链接,并链接到下一个子级和下一个同级,从而形成连接图。

    请注意,本机对象不会在 JavaScript 堆中表示。 缺少表示形式是本机对象大小为零的原因。 而是创建包装器对象。

    每个包装器对象保存对相应本机对象的引用,以便将命令重定向到该对象。 反过来,对象组保存包装器对象。 这不会创建无法收集的循环,因为垃圾回收足够智能,可以释放不再引用其包装器的对象组。 但是,忘记释放单个包装器将保存对整个组和任何关联的包装器的引用。

    周期 是在保留器路径中至少出现两次的节点。 节点的一个外观在保留器路径中较早,该节点的其他外观在保留器路径中较晚出现。

    若要释放内存,最重要的是删除在保留器路径中首先出现的节点。 节点的第二个和可能的后续外观仍显示在 “保留器 ”部分中。

    使用筛选器隐藏周期

    周期显示在堆快照的 “保留器 ”部分中。 为了帮助简化保留器路径, 内存 工具中的 “保留器 ”部分具有用于隐藏周期的筛选器。

    “保留器 ”部分中,通过灰显来指示循环节点。

    在下图的 “筛选器边缘 ”下拉菜单中,未选择 “隐藏循环 ”,因此显示 (灰色) 的循环节点:

    “筛选边缘 ”下拉菜单中,已选择 “隐藏循环 ”,因此不显示循环节点:

    使用筛选器隐藏内部节点

    若要筛选掉内部节点的显示,使其不显示在 “保留器 ”部分中,请在 “筛选边缘 ”下拉菜单中,选择“ 隐藏内部 ”。 内部节点 是特定于 V8 的对象, (Microsoft Edge) 中的 JavaScript 引擎。

    此页面的部分内容是基于 Google 创建和 共享 的工作进行的修改,并根据 Creative Commons 署名 4.0 国际许可 中所述的条款使用。 原始页面 在此处 找到,由 Meggin Kearney (Technical Writer) 创作。

    Creative Commons 许可证 此作品在 Creative Commons 署名 4.0 国际许可下获得许可