DevTools Memory 面板
Memory 面板用于观察堆内存、堆快照、分配趋势和未释放引用,是排查内存泄漏的重要入口。
#type / concept
#status / growing
#tech / dev / frontend
#platform / browser
#resource / chrome-devtools
[!info] related notes
- 所属 MOC: 浏览器开发者工具 MOC
- 前置概念: JS 垃圾回收与内存管理
- 并列概念: Performance 面板, Sources 面板
- 易混淆概念: 浏览器内存泄漏排查
- 关系笔记: 浏览器开发者工具
DevTools Memory 面板
一句话定义
Memory 面板是 Chrome DevTools 中用于拍摄堆快照、分析内存分配趋势、定位未被回收对象的内存分析工具。
核心机制 / 工作原理
它最适合回答什么问题
- 当前页面的堆内存是否持续增长
- 哪类对象占用最大
- 哪些对象被谁持有,为什么没被回收
- 是否存在 Detached DOM、闭包引用、监听器未释放等问题
三种快照类型
| 类型 | 原理 | 适用场景 |
|---|---|---|
| Heap Snapshot | 拍摄当前堆内存的完整快照 | 分析当前内存中有哪些对象、谁引用谁 |
| Allocation instrumentation on timeline | 记录一段时间内的内存分配 | 观察内存增长趋势,定位分配热点 |
| Allocation sampling | 采样分配调用栈 | 性能开销低,适合生产环境长时间采集 |
使用流程
1. 拍摄基线快照
- 打开 DevTools → Memory 面板
- 选择 “Heap snapshot” → 点击 “Take snapshot”
- 记录初始状态
2. 执行可疑操作
- 页面切换、组件挂载卸载、重复打开关闭弹窗等
3. 拍摄第二份快照
- 操作后再拍一份 Heap snapshot
4. 比较快照
- 选择第二份快照,在 Comparison 视图中与基线对比
- 关注 “#Delta” 列:新增对象数量和大小
- 按 “Retained Size” 排序,找到最大的未释放对象
5. 分析保留路径
- 点击对象 → 查看 “Retainers” 面板
- 找到引用链:谁持有这个对象 → 为什么 GC 无法回收
关键指标
| 指标 | 含义 |
|---|---|
| Shallow Size | 对象自身占用的内存 |
| Retained Size | 对象被 GC 回收后能释放的总内存(含其引用的子对象) |
| Distance | 对象到 GC 根的距离 |
最小例子
排查组件卸载后内存未释放:
// 有内存泄漏的代码
class ChartComponent {
constructor() {
this.data = new Array(100000).fill(0);
this.resizeHandler = () => this.render();
window.addEventListener('resize', this.resizeHandler);
}
// 忘记移除事件监听器
destroy() {
// 应该调用 window.removeEventListener('resize', this.resizeHandler)
}
}
Memory 面板排查步骤:
- 拍快照 A
- 创建并销毁 10 次 ChartComponent
- 拍快照 B
- Comparison 视图中看到 ChartComponent 实例数量 = 10
- 查看 Retainers → 发现 resizeHandler 被 window 的事件监听器持有
- 修复:在 destroy 中
removeEventListener
常见内存泄漏模式
| 模式 | 原因 | 在 Memory 面板中的表现 |
|---|---|---|
| 闭包持有大对象 | 闭包引用外部变量,函数未释放 | 大量 Closure 对象,Retained Size 大 |
| Detached DOM | DOM 节点从文档移除但 JS 仍引用 | Heap snapshot 中搜 “Detached” |
| 事件监听器未移除 | addEventListener 后未 removeEventListener | 对象被 EventTarget 的 listener 列表持有 |
| 定时器未清理 | setInterval/setTimeout 回调持有外部引用 | 回调函数及其闭包持续存在 |
| 全局缓存无限增长 | Map/Set/Array 只增不删 | 全局对象下的集合越来越大 |
边界与常见误解
- 内存增长不等于泄漏:可能是缓存策略、GC 时机、或浏览器预分配;需多次快照对比确认
- 单次快照意义有限:只看一份快照无法判断是否有泄漏,必须结合操作前后对比
- GC 时机不可控:拍摄快照前可点击 “Collect garbage” 按钮手动触发 GC,确保结果准确
- Memory 面板给的是”谁还活着”的证据:真正修复通常还要回到 Sources 面板 定位代码
- DevTools 自身会增加内存开销:拍摄快照时会暂停页面,大页面可能卡顿数秒
- Worker 内存不在面板中:Memory 面板只分析主线程堆内存,Web Worker 需单独分析
实用排障顺序
- 先确认是不是正常缓存或短期峰值
- 在关键操作前后各打一份快照
- 对比增长对象和保留路径(Retainers)
- 用 “Allocation on timeline” 看分配趋势确认持续增长
- 回到代码查事件监听、定时器、闭包和全局缓存
- 修复后再拍快照验证