聚合视图

在 Firefox 48 之前,这是堆快照的默认视图。Firefox 48 之后,默认视图是 树状图视图,您可以使用标有“视图:”的下拉菜单切换到聚合视图。

../../../_images/memory-tool-switch-view.png

聚合视图看起来像这样

../../../_images/memory-tool-aggregate-view.png

它以表格的形式显示堆内容的细分。有三种主要方法可以对数据进行分组

您可以使用位于面板顶部的标有“按以下分组:”的下拉菜单在它们之间切换。

面板右上角还有一个名为“过滤器”的框。您可以使用它来筛选显示的快照内容,以便快速查看例如分配了多少特定类的对象。

类型

这是默认视图,看起来像这样

../../../_images/memory-tool-aggregate-view.png

它将堆上的内容分组为类型,包括

  • JavaScript 对象:例如 FunctionArray

  • DOM 元素:例如 HTMLSpanElementWindow

  • 字符串:列为 "strings"

  • JavaScript 源代码:列为“JSScript"

  • 内部对象:例如“js::Shape”。这些以 "js::" 为前缀。

每种类型在表中都有一行,并且按该类型对象占用的内存量排序。例如,在上面的屏幕截图中,您可以看到 JavaScript Object 占用了大部分内存,其次是字符串。

  • “总计”列显示当前分配的每个类别的对象数量。

  • “总字节数”列显示每个类别中对象占用的字节数,以及该数字占该选项卡整个堆大小的百分比。

本节中的屏幕截图来自 怪物示例页面 的快照。

例如,在上面的屏幕截图中,您可以看到

  • 有四个 Array 对象

  • 占总堆的 15%。

在类型名称旁边,有一个包含三个星形排列成三角形的图标

../../../_images/memory-tool-in-group-icon.png

单击此图标以查看该类型的每个实例。例如,Array 的条目告诉我们快照中有四个 Array 对象。如果我们单击星形三角形,我们将看到所有四个 Array 实例

../../../_images/memory-tool-in-group.png

对于每个实例,您可以看到该实例的 保留大小和浅层大小。在这种情况下,您可以看到前三个数组具有相当大的浅层大小(占总堆使用量的 5%)和更大的保留大小(占总堆的 26%)。

右侧是一个仅显示“选择一个项目以查看其保留路径”的面板。如果选择一个项目,您将看到该项目的 保留路径面板

../../../_images/memory-tool-in-group-retaining-paths.png

调用栈

调用栈向您显示在代码中的哪个位置进行了堆分配。

由于跟踪分配会产生运行时开销,因此必须在快照中分配内存之前通过选中“记录调用栈”明确启用它。

然后,您将看到分配对象的函数列表,按其进行分配的大小排序

../../../_images/memory-tool-call-stack.png

此视图的结构非常类似于 调用树 的结构,只是它显示分配而不是处理器采样。因此,例如,第一个条目表示

  • 4,832,592 字节,占总堆使用量的 93%,是在“alloc.js”第 35 行的函数中分配的,**或在该函数调用的函数中分配的**

我们可以使用展开三角形向下钻取调用树,以查找代码进行分配的确切位置。

使用简单的示例更容易解释这一点。对于 DOM 分配示例。此页面运行一个脚本,该脚本创建大量 DOM 节点(200 个 HTMLDivElement 对象和 4000 个 HTMLSpanElement 对象)。

让我们获取分配跟踪

  1. 打开内存工具

  2. 选中“记录调用栈”

  3. 加载 https://firefox-devtools.github.io/performance-scenarios/dom-allocs/alloc.html

  4. 获取快照

  5. 选择“视图/聚合”

  6. 选择“按以下分组/调用栈”



您应该会看到类似以下内容

../../../_images/memory-tool-call-stack.png

这告诉我们,总堆快照的 93% 是在从“alloc.js”第 35 行(我们最初的 createToolbars() 调用)调用的函数中分配的。

我们可以使用展开箭头扩展树,以找出我们分配内存的确切位置

../../../_images/memory-tool-call-stack-expanded.png

这就是“字节数”和“计数”列发挥作用的地方:它们显示在该确切位置的分配大小和分配次数。

因此,在上面的示例中,我们可以看到我们在 createToolbarButton() 中进行了 4002 次分配,占总堆的 89%,位于 alloc.js 第 9 行,位置 23:也就是说,我们创建 <span> 元素的确切位置。

文件名和行号是链接:如果我们单击它,我们将直接转到调试器中的该行



反向调用栈

调用栈视图是从上到下的:它显示在该点或调用树中更深层的点发生的分配。因此,它非常适合于概述程序在哪些地方内存使用量很大。但是,此视图意味着您必须向下钻取很长时间才能找到分配发生的确切位置。

“反向调用栈”视图有助于解决此问题。它提供了程序的自下而上的视图,显示了分配发生的确切位置,并按每个位置的分配大小进行排序。然后,展开箭头将您沿着调用树向上返回到顶层。

让我们看看在选择“反向调用栈”时示例的样子

../../../_images/memory-tool-inverted-call-stack.png

现在,在顶部,我们可以立即看到 createToolbarButton() 调用占页面堆使用量的 89%。

(无可用堆栈)

在上面的示例中,您会注意到 7% 的堆标记为“(无可用堆栈)”。这是因为并非所有堆使用都来自您的 JavaScript。

例如

  • 页面加载的任何脚本都会占用堆空间

  • 有时在堆栈中没有 JavaScript 时会分配对象。例如,DOM Event 对象在 JavaScript 运行和调用事件处理程序之前分配。

许多现实世界的页面将拥有比 7% 高得多的“(无可用堆栈)”份额。