检测javascript闭包内存增长的核心是确认闭包是否被意外长期持有及其捕获的不该留存的对象,需通过chrome devtools memory面板拍摄两次堆快照并比对,重点关注closure的#new增量与retained size异常项,结合retaining tree定位持有者,并展开[[scopes]]查看实际捕获变量。

检测 JavaScript 闭包导致的内存增长,核心是确认“闭包是否被意外长期持有”,以及“它捕获了哪些不该留下的对象”。这不是看有没有闭包,而是看闭包是否在不该存在时仍驻留在内存中,并拖着大体积数据不放。Chrome DevTools 的 Memory 面板是最直接、最可靠的工具,整个过程围绕堆快照比对和引用链追溯展开。
拍两次堆快照做对比
这是定位异常增长的第一步,关键在于控制变量、排除干扰:
- 打开目标页面,按 F12 打开 DevTools,切换到 Memory 面板
- 先点击左上角垃圾桶图标手动触发一次垃圾回收(GC),再点击 “Take heap snapshot” 拍下第一张(Baseline)
- 执行一次疑似引发增长的操作——比如打开并关闭一个弹窗、切换一次路由、刷新一次列表
- 再次手动 GC,然后拍第二张快照
- 在快照列表中选中第二张,右上角下拉菜单切为 “Comparison” 模式
筛选 Closure 并关注关键指标
不是所有 Closure 都有问题,重点看那些“不该增长却持续增长”的项:
- 在 Constructor 列输入 (closure) 筛选
- 重点关注两列:# New(本次操作新增数量)和 Retained Size(该闭包及其捕获对象总共占用的内存)
- 若某 Closure 的 # New 每次操作都稳定增加,且 Retained Size 达几百 KB 甚至 MB 级,就要深挖
- 特别留意名字含 handler、callback、onXXX、bind、create 的闭包,它们常来自事件、定时器或封装逻辑
顺着 Retaining Tree 找持有者
闭包本身内存很小,真正吃内存的是它锁住的对象。Retaining Tree 告诉你“谁不让它走”:
- 双击可疑 Closure 条目,右侧自动展开 Retaining Tree
- 从顶部往下看第一级节点:若显示 window.xxx 或 globalThis.cache,说明被全局变量强持
- 若显示 EventListener 且对应 DOM 节点状态为 Detached,基本就是事件没解绑
- 若指向 setInterval、Map.entries 或某个长期存活的 class 实例,也都是典型泄漏源头
展开 [[Scopes]] 查实际捕获内容
Retainers 只告诉你“谁在引用”,而 [[Scopes]] 才能看清闭包里到底锁住了什么:
- 在 Summary 视图选中该 Closure,右侧 Properties 中找到 [[Scopes]] 并展开
- 进入类型为 Closure 的 scope,查看里面的 Properties
- 这里会列出真实被捕获的变量名和值,比如 data、this.$el、chartInstance
- 如果只用 id 却捕获了整个用户对象,或只读一个属性却拿了整棵 DOM 树,就属于可优化点
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











