Memory面板里怎么判断是不是内存泄漏?
我在用 Chrome DevTools 的 Memory 面板做性能分析,拍了几次快照,看到有些对象数量一直在涨,但不确定是不是真的内存泄漏。比如我反复打开关闭一个弹窗组件,按理说 DOM 和事件监听器应该都被清理了,但快照里还是能看到很多 Detached DOM tree。
我试过手动触发 GC(垃圾回收),也检查过有没有忘记 removeEventListener,但问题还在。有没有什么靠谱的判断方法?比如看 retainers 树还是对比快照时关注哪些字段?
这是我的组件销毁逻辑:
useEffect(() => {
const handleResize = () => { /* ... */ };
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
最靠谱的判断方法是做三次堆快照对比。第一步,打开页面后先拍个快照作为基准。第二步,执行你的操作,比如反复开关弹窗10次。第三步,手动点一下那个小垃圾桶图标触发 GC,然后再拍一个快照。
关键来了,切到 Comparison 视图,对比第一次和最后一次快照,重点看 Delta 列。如果某个对象类型的 Delta 是正数且数量和你操作次数相关,那基本就是泄漏了。比如你开关10次弹窗,发现有10个 Detached DOM tree 没被回收,那就是实锤。
另外还有一个更直观的办法,用 Allocation instrumentation on timeline 模式。录制的时候操作几次,然后看蓝色的柱子。如果操作完停一会儿,蓝色柱子还在那不消失,说明内存没释放掉。
你发的代码本身没问题,useEffect 的清理函数写法是对的。但 Detached DOM tree 不一定是事件监听器的问题,也可能是闭包引用或者第三方库的锅。在 Heap snapshot 里点开那个 Detached DOM node,看下面的 Retainers 面板,顺着引用链找,看是谁死死拽着它不放。
我遇到过一种情况是组件里用了某个 UI 库的弹窗,那库内部往 DOM 节点上挂了 jQuery data,结果销毁时没清理干净。这种只能去翻那个库的 issue 或者自己手动清理。
还有个小技巧,在 Retainers 视图里,红色的节点代表还在 DOM 树里的,黄色的才是真正 Detached 的,别看错了。
希望能帮到你,内存泄漏这玩意儿排查起来确实挺折磨人的。