← [ BACK_TO_NOTES ]

浏览器内存泄漏排查

通过可重复场景、GC 后基线、前后 Heap Snapshot、Retainers 和 allocation trend 判断浏览器内存增长是否为真正泄漏,并定位未释放监听器、Detached DOM、定时器、闭包和无界缓存。

#debug #growing #frontend #browser

[!info] related notes

浏览器内存泄漏排查

先定义什么叫泄漏

内存上涨不等于泄漏。

可能只是:

  • 正常 cache;
  • lazy-loaded data;
  • 浏览器/V8 预留 heap;
  • GC 尚未发生;
  • 用户确实打开了更多内容。

更有意义的泄漏证据是:

执行同一可逆操作多轮,并回到逻辑基线后,本应可回收的对象仍持续被保留。

建立可重复场景

选择一个可重复、最好可恢复的动作:

baseline
→ open dialog/page/chart
→ close/unmount
→ return to baseline

重复多轮。

只有这样才能把“功能正常占用”与“每轮额外残留”分开。

三阶段快照思路

对可逆场景可使用:

  1. baseline snapshot;
  2. 执行动作后的 snapshot;
  3. 退出/卸载并 GC 后的 snapshot。

第三份比第二份更关键:它回答“已经回到逻辑基线后,什么还不该活着却活着”。

找持续增长对象

在 Comparison / statistics 中关注:

  • 某 class/component 实例数量每轮增加;
  • Detached DOM nodes;
  • 大 Array/Map/Set;
  • listener / closure 相关对象;
  • 某第三方库对象。

不要只看总 heap MB。

用 Retainers 找真正根因

泄漏的关键问题不是:

这个对象占多少?

而是:

谁还在持有它,使 GC 无法回收?

沿 Retainers 可能找到:

  • window/document event listener;
  • timer callback;
  • global store/cache;
  • closure;
  • framework registry;
  • detached tree 中的引用。

Allocation Trend

如果快照很难看出分配来源,可记录 allocation trend / sampling,找到哪些调用栈持续产生对象。

Allocation sampling 通常比完整 allocation instrumentation 更轻量,但它仍然是诊断数据,不等于生产 RUM 的通用替代。

高频根因

Event listener

组件卸载但全局 listener 没 remove。

Timer

setInterval / 长期 timer 的 callback 保留闭包。

Detached DOM

DOM 已从 document 移除,但 JS 变量/observer/listener 仍引用。

无界缓存

Map/Array 只增不删,没有 TTL/LRU/容量上限。

Subscription

WebSocket/event bus/store subscription 没 unsubscribe。

修复后必须复测

用相同操作重新跑:

baseline
→ repeat N times
→ return
→ GC
→ snapshot

目标不是让 heap 完全回到同一个字节数,而是:

  • 可疑实例不再线性累积;
  • retain path 消失;
  • 内存增长趋于稳定。

迁移检查

场景:一个 Chart Dialog 每打开关闭 10 次,heap 都高一点。

闭卷完成:

  1. 为什么只看 Task Manager 内存曲线不够?
  2. 怎样建立可重复 baseline?
  3. 为什么需要操作后的快照和回到 baseline 后的快照?
  4. 如何用 Retainers 区分 listener、timer、global cache 三种根因?
  5. 修复后怎样证明“不是碰巧 GC 了”,而是真正停止累积?
创建于 2026/4/9 更新于 2026/9/30
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本