Chrome DevTools Memory
一、认识
Chrome DevTools Memory
提供强大的工具来分析和排查内存泄漏,检测内存泄漏,分析内存分配,优化内存使用。主要通过 内存快照(Heap Snapshot
) 和 实时内存监控 等功能。
二、操作
一、打开 Chrome DevTools
: 首先,确保你已经打开了 Chrome
浏览器的开发者工具。你可以通过右键点击页面元素并选择 检查 或按 F12
来打开开发者工具。在开发者工具中,切换到 Memory
面板,它专门用于内存分析。
二、录制内存快照(Heap Snapshot
): 内存快照用于记录应用程序在特定时刻的内存使用情况,并可以帮助识别持续增长的对象。
-
在
Chrome DevTools
中,点击顶部菜单栏的Memory
面板。 -
在
Memory
面板中,选择Heap Snapshot
模式。 -
点击
Take Snapshot
按钮,生成一份内存快照。 -
生成的快照文件会显示在左侧列表中,包含堆内存中所有对象的详细信息。
-
在代码运行一段时间后,再次点击
Take Snapshot
生成另一个快照。
三、模拟泄漏
假设有一个简单的内存泄漏示例: 全局变量 leaks
数组被持续地推入新对象(如 push
操作),而旧对象没有被释放, 每次请求造成内存持续增加。但是没有释放的操作。
const leaks = [];
setInterval(() => {
leaks.push(new Array(1000000).fill('*')); // 内存泄漏
}, 5000);
1. 打开 Chrome DevTools Memory
2. 录制内存快照: 一般而言,记录内存快照越多,越容易发现内存泄漏的点。
-
启动程序录制快照: 在程序启动后,点击
Take Snapshot
,记录初始内存情况。 -
访问多次应用程序: 代码执行,内存泄漏触发, 记录第二次内存快照
-
再多次访问应用程序: 代码执行,内存泄漏触发, 记录第三次内存快照
-
再多次访问应用程序: 代码执行,内存泄漏触发, 记录第四次内存快照
四、快照分析
4.1 比较快照
比较多个内存快照: 内存使用量从 5.5 MB → 37.7 MB → 85.7 MB
,说明内存占用持续增长。这种情况通常意味着存在内存泄漏,特别是当没有释放的对象持续堆积时。
从截图中可以看到左侧有 3 个快照:
Snapshot 1(5.5 MB)
Snapshot 2(37.7 MB)
Snapshot 3(85.7 MB)
4.2 Summary 定位可疑对象
看这个视图的时候一般会先对 Retained Size
进行排查,然后观察其中对象的大小与数量,有经验的工程师,可以快速判断出某些对象数量异常。在这个视图中除了关心自己定义的一些对象之外, 一些容易发生内存泄漏的对象也需要注意如:
-
TCP
-
Socket
-
EventEmitter
-
global
4.3 Comparison 定位可疑对象
如果通过 Summary
视图, 不能定位到问题这时我们一般会使用 Comparison
视图。通过这个视图我们能对比两个堆快照中对象个数、与对象占有内存的变化;
通过这些信息我们可以判断在一段时间(某些操作)之后,堆中的对象与内存变化的数值,通过这些数值我们可以找出一些异常的对象。通过这些对象的名称属性或作用可以缩小我们内存泄漏的排查范围。
在 Comparison
视图中选择两个堆快照,并在它们之间进行比较。您可以查看哪些对象在两个堆快照之间新增,哪些对象在两个堆快照之间减少,以及哪些对象的大小发生了变化。Comparison
视图还允许查看对象之间的关系,以及对象的详细信息,如类型、大小和引用计数。通过这些信息,可以了解哪些对象是导致内存泄漏的原因。在 Comparison
视图中查找可疑对象,用于比较当前快照和之前快照的内存变化。主要关注 Comparison.Delta
对象数量的净增量和 Comparison.Size Delta
对象分配的内存变化量。
Array @104125
和其他几个数组显示 New: 13
,Delta: +13
,并且总分配的内存是 32,000,000(32 MB)
。这些数组占据了大部分内存,说明它们是内存增长的主要来源。关键是这些数组 持续增长且没有释放,表明这里可能存在泄漏。
4.4 Comparison 定位可疑引用链
其实可以找到一些代码中的变量,如下所示:
[6] in Array @89457
leaks in system / Context @89455 80 000 66493 %
说明 leaks
在可疑对象中占比非常大。
五、DevTools Comparison
Comparison
用于将当前快照与之前的某个快照进行比较,显示内存对象的增量和变化。通过这个视图可以直观地看到哪些对象被分配了新的内存,但没有被释放(即内存持续增加)。
5.1 Constructor
Constructor
显示内存中对象的类型或构造函数(Constructor
)。比如 Array
、Object
、Node
、Detached Node
等。每一行表示一种类型的对象集合。
5.2 New
# New
: 从上一个快照到当前快照,新分配的对象数量。# New
可以观察到观察内存中哪些类型的对象正在快速分配。例如,如果 Array
的 # New
值持续增加但没有减少,说明新数组对象在不断创建,但未被释放。可以用来定位新增对象的来源。
5.3 Deleted
# Deleted
: 从上一个快照到当前快照,被删除的对象数量。# Deleted
可以观察垃圾回收器(GC
)释放了哪些对象。如果某类对象的 # Deleted
为 0
,说明这类对象没有被释放,可能导致内存泄漏。可以用来分析哪些对象未被正确清理或回收。
5.4 Delta
# Delta
: # New
和 # Deleted
的差值,表示对象数量的净增量。如果为正值,表示对象数量增加了;如果为负值,表示对象数量减少了;如果为 0
, 表示对象数量没有变化。可以用来快速定位那些对象数量持续增长的类型。如果某类对象的 # Delta
持续为正,说明存在内存泄漏的可能。
5.5 Alloc. Size
Alloc. Size
: 表示对象分配的总内存大小。显示某类对象总共占用了多少内存。通过这个值,可以快速定位占用内存较大的对象类型。可以用来找出高内存占用的对象,如大型数组或字符串。
5.6 Freed Size
Freed Size
: 表示在当前快照中,被垃圾回收器(GC
)释放的内存大小。观察 GC
是否正常回收了不再需要的对象。如果某类对象的 Freed Size
为 0
,但内存持续增长,说明 GC
未能回收这些对象,可能存在引用问题。可以用来判断 GC
的清理效果,找出无法回收的对象。
5.7 Size Delta
Size Delta
: 表示对象分配的内存变化量。如果为正值,内存增加; 如果为负值,内存减少; 如果为 0
, 内存没有变化。快速查看哪些对象类型导致了内存的增长。如果某类对象的 Size Delta
持续为正,说明存在泄漏。
5.8 Retainers
Retainers
面板: 底部区域,显示 引用链(Retainers
),显示某个对象被哪些对象引用,为什么无法被回收。通过 Retainers
面板可以找到泄漏对象的根源。例如,一个数组对象被某个全局变量引用,导致无法释放。可以找出对象被哪些变量或函数引用,分析内存泄漏的原因。
Retainers
指标如下:
-
Distance
: 从根对象(如全局变量、堆栈根节点)到当前对象的引用距离。数值越小,表示对象离根引用越近,越可能无法被回收。 -
Shallow Size
: 当前对象本身占用的内存大小。显示单个对象的内存消耗。用于分析大对象是否导致了内存泄漏。 -
Retained Size
: 如果该对象被保留,它所占用的总内存,包括它所引用的对象的内存。Retained Size
大的对象通常是导致内存泄漏的主要原因。找到这些对象后,可以通过代码分析为什么它们没有被释放。