Debugger.Memory

调试器 API 可以帮助工具通过各种方式观察被调试者的内存使用情况

  • 它可以在分配每个新对象时用 JavaScript 调用栈对其进行标记。

  • 它可以记录所有对象分配,生成一个 JavaScript 调用栈流,其中包含已发生分配的调用栈。

  • 它可以计算被调试者所属项目的 *清单*,以各种方式对项目进行分类,并生成项目计数。

如果 dbg 是一个 Debugger 实例,那么 dbg.memory 的方法和访问器属性控制 dbg 如何观察其被调试者的内存使用情况。 dbg.memory 对象是 Debugger.Memory 的一个实例;其继承的访问器和方法在下面描述。

分配站点跟踪

如果满足以下条件,JavaScript 引擎会用分配该对象的调用栈标记每个新对象:

给定一个引用某个对象的 Debugger.Object 实例 dobjdobj.allocationSite 返回一个保存的调用栈,指示 dobj 的引用在哪里分配。

分配日志记录

如果 dbg 是一个 Debugger 实例,并且 dbg.memory.trackingAllocationSites 设置为 true,那么 JavaScript 引擎会记录 dbg 的被调试者代码分配的每个对象。您可以通过调用 dbg.memory.drainAllocationsLog 获取当前日志。您可以通过设置 dbg.memory.maxAllocationsLogLength 控制日志大小的限制。

清单

*清单* 是对属于特定 Debugger 的被调试者的所有可达内存项的图进行的完整遍历。它会生成这些项目的计数,并按各种标准细分。如果 dbg 是一个 Debugger 实例,您可以调用 dbg.memory.takeCensus 对其被调试者的所有物进行清单统计。

Debugger.Memory.prototype 对象的访问器属性

如果 dbg 是一个 Debugger 实例,那么 dbg.memory 是一个 Debugger.Memory 实例,它从其原型继承以下访问器属性

trackingAllocationSites

一个布尔值,指示此 Debugger.Memory 实例是否在分配每个对象时捕获 JavaScript 执行栈。此访问器属性同时具有 getter 和 setter:对其进行赋值可以启用或禁用分配站点跟踪。读取访问器会在 Debugger 捕获对象分配的栈时生成 true,否则生成 false。在新的 Debugger 中,分配站点跟踪最初处于禁用状态。

赋值是可能失败的:如果 Debugger 无法跟踪分配站点,它会抛出一个 Error 实例。

您可以使用 Debugger.Object.prototype.allocationSite 访问器属性检索给定对象的分配站点。

allocationSamplingProbability

一个介于 0 和 1 之间的数字,表示应将每个新分配记录到分配日志中的概率。0 等效于“从不”,1 等效于“总是”,而 .05 则表示“二十分之一”。

默认为 1,或记录每次分配。

请注意,在多个 Debugger 实例观察全局对象作用域内的相同分配的情况下,将使用所有 Debugger 的 allocationSamplingProbability 的最大值。

maxAllocationsLogLength

一次在分配日志中累积的最大分配站点数。此访问器可以被获取和存储。其默认值为 5000

allocationsLogOverflowed

如果自上次调用 [drainAllocationsLog][#drain-alloc-log] 以来,分配次数超过 [maxAllocationsLogLength][#max-alloc-log],并且某些数据丢失,则返回 true。否则返回 false

Debugger.Memory 处理程序函数

类似于 Debugger 的处理程序函数,Debugger.Memory 继承存储处理程序函数的访问器属性,供 SpiderMonkey 在被调试者代码中发生给定事件时调用。

与 Debugger 的挂钩不同,Debugger.Memory 的处理程序的返回值并不重要,会被忽略。处理程序函数接收 Debugger.Memory 的拥有 Debugger 实例作为其 this 值。拥有 Debugger 的 uncaughtExceptionHandler 仍然会为 Debugger.Memory 挂钩中抛出的错误触发。

在新 Debugger.Memory 实例中,这些属性中的每一个最初都是 undefined。分配给调试处理程序的任何值必须是函数或 undefined;否则会抛出 TypeError。

处理程序函数在事件发生的同一线程中运行。它们在属于它们的区室中运行,而不是在被调试者的区室中运行。

onGarbageCollection(statistics)

刚刚完成了一个跨越一个或多个被调试者的垃圾回收周期。

statistics 参数是一个包含有关 GC 周期信息的对象。它具有以下属性

collections

collections 属性的值是一个数组。因为 SpiderMonkey 的收集器是增量的,所以完整的收集周期可能包含多个离散的收集片段,并且 JS mutator 交错运行。对于发生的每个收集片段,collections 数组中都有一个条目,其形式如下

{
  "startTimestamp": timestamp,
  "endTimestamp": timestamp,
}

这里 timestamp 值是 GC 片段的开始和结束事件的时间戳。

reason

一个非常短的字符串,描述触发收集的原因。已知值包括以下内容

  • “API”

  • “EAGER_ALLOC_TRIGGER”

  • “DESTROY_RUNTIME”

  • “LAST_DITCH”

  • “TOO_MUCH_MALLOC”

  • “ALLOC_TRIGGER”

  • “DEBUG_GC”

  • “COMPARTMENT_REVIVED”

  • “RESET”

  • “OUT_OF_NURSERY”

  • “EVICT_NURSERY”

  • “FULL_STORE_BUFFER”

  • “SHARED_MEMORY_LIMIT”

  • “PERIODIC_FULL_GC”

  • “INCREMENTAL_TOO_SLOW”

  • “DOM_WINDOW_UTILS”

  • “COMPONENT_UTILS”

  • “MEM_PRESSURE”

  • “CC_WAITING”

  • “CC_FORCED”

  • “LOAD_END”

  • “PAGE_HIDE”

  • “NSJSCONTEXT_DESTROY”

  • “SET_NEW_DOCUMENT”

  • “SET_DOC_SHELL”

  • “DOM_UTILS”

  • “DOM_IPC”

  • “DOM_WORKER”

  • “INTER_SLICE_GC”

  • “REFRESH_FRAME”

  • “FULL_GC_TIMER”

  • “SHUTDOWN_CC”

  • “USER_INACTIVE”

nonincrementalReason

如果 SpiderMonkey 的收集器确定无法增量收集垃圾,并且必须一次性执行完全 GC,则这是一个简短的字符串,描述它确定完全 GC 必要的理由。否则,将返回 null。已知值包括以下内容

  • “GC 模式”

  • “malloc 字节触发”

  • “分配触发”

  • “请求”

gcCycleNumber

GC 周期的“编号”。不对应于已运行的 GC 周期的数量,但保证单调递增。

Debugger.Memory.prototype 对象的函数属性

内存使用分析揭示实现细节

内存分析可能会产生令人惊讶的结果,因为对内容 JavaScript 透明的浏览器实现细节通常会对内存消耗产生可见的影响。Web 开发人员需要了解其页面在真实浏览器上的实际内存消耗,因此工具公开这些行为是正确的,只要以有助于开发人员做出有关自身代码决策的方式进行即可。

本节介绍 Firefox 的实际行为与 Web 平台指定行为之间存在差异的一些领域。

对象

SpiderMonkey 对象通常使用的内存少于天真的“具有属性的属性表”模型所暗示的。例如,许多对象通常具有相同的属性集,只有属性值在各个对象之间有所不同。为了利用这种规律性,具有相同属性集的 SpiderMonkey 对象可以共享其属性元数据;只有属性值直接存储在对象中。

如果活动索引集密集,则数组对象也可以被优化。

字符串

SpiderMonkey 有三种字符串表示形式

  • 普通:字符串的文本计入其大小。

  • 子字符串:字符串是其他字符串的子字符串,并指向该字符串以进行存储。这种表示形式可能会导致一个小字符串保留一个非常大的字符串。但是,字符串本身消耗的内存是一个小的常数,与它的大小无关,因为它是对较大字符串、起始位置和长度的引用。

  • 字符串连接:当被要求连接两个字符串时,SpiderMonkey 可能会选择延迟复制字符串数据,并将结果表示为指向这两个原始字符串的指针。同样,这样的字符串保留其他字符串,但字符串本身消耗的内存是一个小的常数,与它的长度无关,因为它是一对指针。

SpiderMonkey 会在适当的时候将字符串从更复杂的形式转换为更简单的形式。这样的转换通常会增加内存消耗。

SpiderMonkey 在所有网页和浏览器 JS 之间共享一些字符串。这些共享的字符串称为原子,不包含在字符串计数的统计中。

脚本

SpiderMonkey 采用了一种复杂、混合的 JavaScript 代码表示形式。内存中保留了四种表示形式

  • 源代码。SpiderMonkey 保留了大多数 JavaScript 源代码的副本。

  • 压缩源代码。SpiderMonkey 会压缩 JavaScript 源代码,并在需要时解压缩。启发式算法决定保留未压缩代码的时间。

  • 字节码。这是 SpiderMonkey 对 JavaScript 的解析表示。字节码可以直接解释,或用作即时编译器的输入。源代码按需解析为字节码;从未调用的函数永远不会被解析。

  • 机器码。SpiderMonkey 包括几个即时编译器,每个编译器都将 JavaScript 源代码或字节码转换为机器码。启发式算法决定编译哪些代码以及使用哪个编译器。机器代码可能会响应内存压力而被丢弃,并在需要时重新生成。

此外,SpiderMonkey 还会跟踪变量和对象属性中出现了哪些类型的值。这些类型信息可能很大。

在统计中,所有各种形式的 JavaScript 代码都放在"script"类别中。类型信息计入"types"类别。

源元数据

从文件生成

js/src/doc/Debugger/Debugger.Memory.md

水印

sha256:2c1529d6932efec8c624a6f1f366b09cb7fce625a6468657fab81788240bc7ae

变更集

e91b2c85aacd