文章

Chrome DevTools

模拟移动设备 CPU

与台式机和笔记本电脑相比,移动设备的 CPU 性能要低得多。每次分析网页时,您都可以使用 CPU 节流来模拟网页在移动设备上的性能。

  1. 在开发者工具中,点击性能标签页。
  2. 确保已选中屏幕截图复选框。
  3. 点击 Capture Settings 图标 17144541138441714454112952.png。 开发者工具会显示与捕获性能指标的方式相关的设置。
  4. 对于 CPU,选择 2 倍减速。开发者工具会限制您的 CPU 速度,使其比平常慢 2 倍。

17144541678501714454167835.png

内存

内存面板可让您捕获三种配置文件类型。以下配置文件类型会显示不同角度的快照,并提供不同的效用:

  • 堆快照。堆快照显示了您网页的 JavaScript 对象和相关 DOM 节点之间的内存分布情况。
  • 时间轴上的分配插桩。分配时间轴显示了插桩的 JavaScript 内存分配随时间的变化情况。记录配置文件后,您可以选择一个时间间隔,以查看在其中分配且在记录结束时仍处于有效状态的对象。使用此配置文件类型隔离内存泄漏。
  • 分配采样。使用采样方法记录内存分配。这种性能分析类型的性能开销极小,可用于长时间运行的操作。它能够非常准确地估算分配情况,并按 JavaScript 执行堆栈细分结果。

Object sizes - 对象大小

将内存视为包含基元类型(如数字和字符串)和对象(关联数组)的图表。它在视觉上可以表示为由许多互连点的图表,如下所示:

17144546038481714454603731.png

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

  • 直接通过对象本身。
  • 通过保留对其他对象的引用隐式保存,从而阻止这些对象被垃圾回收器(简称 GC)自动处置。

使用开发者工具中的堆性能分析器(一种用于调查“Profiles”下所发现内存问题的工具)时,您可能会看到一些不同的信息列。

浅层大小 ( Shallow Size )保留大小(Retained Size)这两个选项较为突出,但它们分别代表什么?

Shallow Size - 浅层大小

这是对象本身占用的内存大小

典型的 JavaScript 对象会预留一些内存用于说明和存储立即值。

通常,只有数组和字符串具有明显的浅层大小。

不过,字符串和外部数组的主存储空间通常位于渲染器内存中,只在 JavaScript 堆上公开一个小的封装容器对象。

渲染程序内存是渲染被检查页面的进程的所有内存:

  • 原生内存 + 页面的 JS 堆内存 + 页面启动的所有专用工作器的 JS 堆内存。

不过,即使是小型对象,也可以通过阻止其他对象被自动垃圾回收进程处置间接占用大量内存。

Retained Size - 保留大小

这是对象本身以及无法再从GC roots 访问的依赖对象后被释放的内存大小。

GC roots 由在从原生代码对 V8 以外的 JavaScript 对象进行引用时创建(本地或全局)的句柄组成。所有这些句柄都可以在堆快照中找到:GC roots > 句柄作用域GC roots > 全局句柄

如果在没有深入探讨浏览器实现细节的情况下,对本文档中的句柄进行说明,可能会让人感到困惑。您无需担心 GC 根和句柄。

存在许多内部 GC 根,其中大多数用户不感兴趣。从应用的角度来看,有以下几种根:

  • Window 全局对象(在每个 iframe 中)。堆快照中有一个距离字段,该字段是距窗口的最短保留路径上的属性引用数量。
  • 文档 DOM 树,由可通过遍历文档到达的所有原生 DOM 节点组成。并非所有此类代码都有 JS 封装容器,但如果它们有,则封装容器会在文档处于活动状态时处于活动状态。
  • 有时,对象可能会被调试程序上下文和开发者工具控制台保留(例如,在控制台评估后)。在调试程序中清除控制台并移除活跃断点,创建堆快照。

内存图从根开始,根可以是浏览器的 window 对象,也可以是 Node.js 模块的 Global 对象。您无法控制此根对象的垃圾回收方式。

17144550618461714455061225.png

Objects retaining tree - 对象保留树

堆是一个由互连的对象组成的网络。在数学领域,此结构称为“图”或“内存图”。图表由通过边连接的节点构造而成,这两者均具有给定标签。

  • Nodes (对象)使用用于构建节点的构造函数的名称进行标记。
  • Edges 使用 属性的名称进行标记。

在下面的堆分析器记录中看到的一些引人注目的内容包括距离,即与 GC Roots 的 Distance 。

如果几乎所有同类对象之间的距离相等,而有些对象之间的距离较大,则有必要调查一下。

17144553558451714455354946.png

为函数命名以区分闭包

例如,以下代码不使用命名函数:

1
2
3
4
5
6
7
8
9
function createLargeClosure() {
  var largeStr = new Array(1000000).join('x');

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

  return lC;
}

虽然此示例包含:

1
2
3
4
5
6
7
8
9
function createLargeClosure() {
  var largeStr = new Array(1000000).join('x');

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

  return lC;
}

17144554938441714455493742.png

V8 详细信息

介绍一些专门对应于 V8 JavaScript virtual machine (V8 VM or VM).的内存相关主题

JavaScript 对象表示法

有三种基元类型:

  • Numbers (例如3.14159..)
  • Booleans(true 或 false)
  • Strings (e.g., ‘Werner Heisenberg’)

它们无法引用其他值,并且始终是叶或终止节点。

Numbers 可以存储为:

  • 一个称为 small integers (SMIs) 的 31 位直接整数值,或者
  • heap numbers 指向的 堆对象。Heap numbers 用于存储不适合 SMI 形式的值(例如“双精度”值),或者需要将值“装箱”时(例如在值上设置属性时)。

Strings 可存储在以下位置:

  • VM heap
  • 渲染程序内存外部的变量。系统会创建一个封装容器对象并用于访问外部存储空间,例如,在外部存储空间中存储脚本源和其他从网页接收的内容,而不是将其复制到虚拟机堆上。

新 JavaScript 对象的内存是从专用 JavaScript 堆(或虚拟机堆)分配的。

这些对象由 V8 的垃圾回收器管理,因此,只要至少有一个对它们的强引用,它们就会保持活跃状态。

Native objects

Native objects 是 JavaScript 堆之外的任何对象。与堆对象相反,原生对象在其生命周期内不由 V8 垃圾回收器管理,并且只能使用其 JavaScript 封装容器对象从 JavaScript 访问。

Cons string

Cons string 是一个对象,由存储并联接的成对字符串组成,是串联的结果。仅在需要时联接 cons 字符串内容。例如,需要构建已联接字符串的子字符串时。

例如,如果您将 ab 串联起来,会得到一个表示串联结果的字符串 (a, b)。

如果您稍后将 d 与该结果串联,会得到另一个 cons 字符串 ((a, b), d)。

Arrays

数组是具有数字键的对象。它们在 V8 虚拟机中被广泛使用,用于存储大量数据。

用作字典的成组键值对由数组提供支持。

典型的 JavaScript 对象可以是以下两种数组类型之一,用于存储:

  • 命名的属性,以及
  • 数字元素

如果属性数量非常少,则这些属性可以存储在 JavaScript 对象本身内部。

Map

用于描述对象种类及其布局的对象。

例如,映射用于描述隐式对象层次结构,以实现快速属性访问

Object groups

每个原生对象组都由保持对彼此的相互引用的对象组成。

例如,假设有一个 DOM 子树,其中每个节点都有一个指向其父节点的链接,并链接到下一个子节点和下一个同级节点,从而形成一个连通图。

请注意,JavaScript 堆中未显示原生对象,这就是它们的大小为零的原因。而是创建封装容器对象。

每个封装容器对象都会存储对相应原生对象的引用,用于将命令重定向到自身,对象组会独立保存封装容器对象

但是,这不会造成不可回收的循环,因为 GC 非常智能,可以释放封装容器不再引用的对象组。

但是,忘记释放单个封装容器将保留整个组和关联的封装容器。

Allocation Profiler Tool

查找未正确进行垃圾回收的对象

17144604788481714460478376.png

本文由作者按照 CC BY 4.0 进行授权