打开要分析的网页。 例如,在新窗口或选项卡中打开
“散点对象
”演示页。
若要打开 DevTools,请右键单击网页,然后选择“
检查
”。 或者,按
Ctrl+Shift+I
(Windows、Linux)或
Command+Option+I
(macOS)。 DevTools 随即打开。
在 DevTools 的
“活动栏”上
,选择“
内存
”选项卡。如果该选项卡不可见,请单击“
更多工具
(
) 按钮。
在
“选择分析类型
”部分中,选择“
堆快照
”选项按钮。
在
“选择 JavaScript VM 实例
”下,选择要分析的 JavaScript VM。
单击“
采取快照
”按钮:
新边栏项下面的数字显示可访问的 JavaScript 对象的总大小。 若要详细了解堆快照中的对象大小,请参阅
内存术语
中的
对象大小和距离
。
快照仅显示内存图中可从全局对象访问的对象。 快照始终从垃圾回收开始。
采取另一个快照
若要在
内存
工具中显示另一个快照,请在边栏中单击现有快照上方的
“配置文件
”:
在
“摘要”
视图中展开构造函数后,将显示所有构造函数的实例。 对于每个实例,浅表大小和保留大小显示在相应的列中。 字符后面的
@
数字是对象的唯一 ID,允许你基于每个对象比较堆快照。
构造函数条目
内存
工具中的
“摘要”
视图列出了对象构造函数组:
若要查找泄漏的对象,请比较多个快照。 在 Web 应用程序中,通常执行操作,然后反向操作不应导致内存中的更多对象。 例如,打开文档然后关闭文档时,内存中的对象数应与打开文档之前相同。
若要验证某些操作不会造成泄漏,请执行以下操作:
在执行操作之前,先将堆快照。
执行操作。 也就是说,以某种可能导致泄漏的方式与页面交互。
执行反向操作。 也就是说,执行相反的交互并重复几次。
获取第二个堆快照。
在第二个堆快照中,将视图更改为
“比较
”,并将其与
快照 1
进行比较。
在
“比较”
视图中,将显示两个快照之间的差异:
“保留器”部分
“保留器
”部分显示在
“内存”
工具的底部,并显示指向所选对象的所有对象。 在
“摘要”、“
包含
”或
“比较
”视图中选择其他对象时,将更新
“保留器
”部分。
在以下屏幕截图中,在
“摘要”
视图中选择了一个字符串对象,“
保留器
”部分显示该字符串由
x
类实例的
Item
属性保留(在
example-03.js
文件中找到):
在
“保留器
”部分中,分析保留所选对象的对象时,可能会遇到
周期
。 当同一对象在所选对象的保留器路径中多次出现时,会发生周期。 在
“保留器
”部分中,通过灰显来指示已循环的对象。
为了帮助简化保留器路径,请在“
保留器
”部分中隐藏周期,方法是单击“
筛选器边缘
”下拉菜单,然后选择“
隐藏循环
”:
内部节点
是特定于 V8 的对象, (Microsoft Edge) 中的 JavaScript 引擎。
若要在
“保留器
”部分隐藏内部节点,请在
“筛选器边缘
”下拉菜单中,选择“
隐藏内部
节点”。
按节点类型筛选堆快照
使用筛选器专注于堆快照的特定部分。 在
内存
工具中快照查看堆中的所有对象时,可能很难专注于特定对象或保留路径。
若要仅关注特定类型的节点,请使用右上角的
“节点类型”
筛选器。 例如,若要仅查看堆中的数组和字符串对象,快照:
若要打开
“节点类型
”筛选器,请单击右上角的“
默认值
”。
选择
“数组
”和“
字符串”
条目。
堆快照更新为仅显示数组和字符串对象:
发现 DOM 泄漏
内存
工具能够显示浏览器本机对象 (DOM 节点、CSS 规则) 和 JavaScript 对象之间有时存在的双向依赖关系。 这有助于发现由于内存中被遗忘的分离 DOM 节点而发生的内存泄漏。
对于分离的元素,另请参阅下面的
查找 DOM 树内存泄漏 (“堆快照”分析类型>分离)
。
请考虑以下 DOM 树:
下面的代码示例创建 JavaScript 变量
treeRef
和
leafRef
,它们引用树中的两个 DOM 节点:
// Get a reference to the #tree element.
const treeRef = document.querySelector("#tree");
// Get a reference to the #leaf element,
// which is a descendant of the #tree element.
const leafRef = document.querySelector("#leaf");
在以下代码示例中 <div id="tree">
,元素从 DOM 树中删除:
// Remove the #tree element from the DOM.
document.body.removeChild(treeRef);
<div id="tree">
无法对元素进行垃圾回收,因为 JavaScript 变量treeRef
仍然存在。 变量 treeRef
直接引用 元素 <div id="tree">
。 在以下代码示例中 treeRef
, 变量为 null:
// Remove the treeRef variable.
treeRef = null;
元素 <div id="tree">
仍无法进行垃圾回收,因为 JavaScript 变量 leafRef
仍然存在。 属性 leafRef.parentNode
引用 <div id="tree">
元素。 在以下代码示例中 leafRef
, 变量为 null:
// Remove the leafRef variable.
leafRef = null;
此时, <div id="tree">
可以垃圾回收元素。
treeRef
和 leafRef
必须首先为 null,才能对 元素下的<div id="tree">
整个 DOM 树进行垃圾回收。
演示网页:示例 6:DOM 节点泄漏
若要了解 DOM 节点可能泄漏的位置以及如何检测此类泄漏,请打开示例网页示例 6:在新窗口或选项卡中 泄漏 DOM 节点 。
演示网页:示例 9:DOM 泄漏大于预期
若要查看 DOM 泄漏可能大于预期的原因,请在新窗口或选项卡中打开示例网页 示例 9:DOM 泄漏大于预期 。
在边栏中,第二个快照下面的数字应大于低于第一个快照的数字。 这表示在单击“ 带 eval 的关闭 ”按钮后,网页正在使用更多内存。
在第二个堆快照中,将视图更改为“比较”,然后将第二个堆快照与第一个堆快照进行比较。
“比较”视图显示已在第二个堆中创建新字符串快照:
若要轻松区分堆快照中的 JavaScript 闭包,请提供函数名称。
以下示例使用未命名的函数返回 largeStr
变量:
function createLargeClosure() {
const largeStr = 'x'.repeat(1000000).toLowerCase();
// This function is unnamed.
const lC = function() {
return largeStr;
return lC;
以下示例为 函数命名,以便更轻松地区分堆快照中的闭包:
function createLargeClosure() {
const largeStr = 'x'.repeat(1000000).toLowerCase();
// This function is named.
const lC = function lC() {
return largeStr;
return lC;
将字符串从堆快照保存并导出到 JSON
在内存工具中获取堆快照时,可以将快照中的所有字符串对象导出到 JSON 文件中。 在“内存”工具的“构造函数”部分中,单击条目旁边的(string)
“全部保存到文件”按钮:
查找 DOM 树内存泄漏 (“堆快照”分析类型>分离)
查找和显示网页上所有分离元素的一种方法是使用内存工具的堆快照分析类型,然后在“按类筛选”文本框中键入“分离”,如下所示。 另请参阅修复内存问题中的用于调查分离元素的工具。
以下代码生成分离的 DOM 节点:
var detachedTree;
function create() {
var ul = document.createElement('ul');
for (var i = 0; i < 10; i++) {
var li = document.createElement('li');
ul.appendChild(li);
detachedTree = ul;
document.getElementById('create').addEventListener('click', create);
此代码创建包含 10 li
个ul
子级的节点。 节点由代码引用,但它们在 DOM 树中不存在,因此每个节点都是分离的。
堆快照是标识分离节点的一种方法。 堆快照显示在快照时间点页面的 JS 对象和 DOM 节点之间的内存分布方式。
使用“堆快照”分析类型查找分离的元素
若要使用堆快照分析类型查找分离的元素,
在新窗口或选项卡中打开网页,例如 “分离元素”演示网页。
右键单击网页,然后选择“ 检查”。 或者,按 Ctrl+Shift+I(Windows、Linux)或 Command+Option+I (macOS)。
DevTools 随即打开。
在 DevTools 的 “活动栏”中,选择 “内存 (
) 工具。
如果该选项卡不可见,请单击“ 更多工具 (
) 按钮,然后选择“ 内存”。 此时会打开 “内存 ”工具:
显示某些消息后,单击演示网页中的 “停止 ”按钮。
每条消息都是 <div class="message">
类的 Room 1 实例引用的 Room
元素。 网页 DOM 树中没有分离的元素,因为所有消息元素都附加到 Room 类的当前 Room 1 实例。
更改为 Room 类的不同实例,使元素分离:
在演示网页中,单击“ 会议室 2 ”按钮,该按钮对应于类的另一个实例 Room
。
在网页中,消息消失:
为 Room 类的 Room 1 实例生成的消息 (<div class="message">
元素) 不再附加到 DOM,但仍由 Room 类的 Room 1 实例引用。 它们是分离的元素,这可能会导致内存泄漏,除非网页将再次使用这些元素。
获取分离元素的列表:
在 DevTools 的 内存 工具中,单击“ 回收垃圾 ” (
) 图标:
标识引用特定分离元素的 JavaScript 代码:
在堆快照中,展开“分离”对象(如“分离的 <div>”),然后选择“分离<的 div class=”message“>节点。
信息显示在内存工具底部的“保留器”窗格中。
在“保留器”窗格中,单击“room.js:13
数组”下“会议室中未装载”项的链接。
“源”工具随即打开,显示 room.js
,滚动到第 13 行:
此页面的某些部分是根据 Google 创建和共享的作品所做的修改,并根据 Creative Commons Attribution 4.0 International License 中描述的条款使用。
原始页面 在此处 找到,由 Meggin Kearney 创作。