记录一次前端内存泄漏排查经历

7,850 阅读4分钟

对于前端的“内存泄漏”这个东西,说实话我只在书上看到过:

闭包、匿名函数和事件绑定尤其容易造成内存泄漏。

然而这些操作造成的“内存泄漏”究竟是什么样子的?如何排查?虽然很好奇,却不得而知。直到这次公司应用频繁出现浏览器崩溃的情况,我受命解决这个问题,才开始研究内存泄漏的排查方法,及排查工具。

如何判断内存泄漏

我司的应用在客户现场反馈过来的问题是:在某几台固定的电脑上频繁出现浏览器崩溃的情况。我问现场要了机器的配置,发现这些问题机器的可用运行内存,连三个 G 都没有,在别的内存较大的机器上这种崩溃的情况较少发生。

如果浏览器频繁出现“崩溃”、“卡顿”等情况,则基本可以判断为浏览器内存占用较高,而 CPU 分配给浏览器的内存空间是很小且有限的,如果内存占用濒临极限,就会出现卡顿,如果内存占用溢出,浏览器就会崩溃。

工具排查

Chrome 浏览器,F12 打开开发者工具,涉及公司机密,我就先找了阮一峰老师的 ES6 网站演示。

Performance

点击这个按钮启动记录,然后切换到网页进行操作,录制完成后点击 stop 按钮,开发者工具会从录制时刻开始记录当前应用的各项数据情况

选中JS Heap,下面展现出来的一条蓝线,就是代表了这段记录过程中,JS 堆内存信息的变化情况。

关于“堆”和“栈”的概念,简单的说,开发者创建的对象都在堆里,栈空间是留给系统来分配的

有大佬说,根据这条蓝线就可以判断是否存在内存泄漏的情况:如果这条蓝线一直成上升趋势,那基本就是内存泄漏了

其实我觉得这么讲有失偏颇,JS 堆内存占用率上升并不一定就是内存泄漏,只能说明有很多未被释放的内存而已,至于这些内存是否真的在使用,还是说确实是内存泄漏,还需要进一步排查。

memory

开发者工具的 Memory 选项,可以更精确地定位内存使用情况。

当生成了第一个快照的时候,开发者工具窗口已经显示了很详细的内存占用情况。

字段解释:

  • Constructor — 占用内存的资源类型
  • Distance — 当前对象到根的引用层级距离
  • Shallow Size — 对象所占内存(不包含内部引用的其它对象所占的内存)(单位:字节)
  • Retained Size — 对象所占总内存(包含内部引用的其它对象所占的内存)(单位:字节)

将每项展开可以查看更详细的数据信息。

我们再次切回网页,继续操作几次,然后再次生成一个快照。

这边需要特别注意这个 #Delta ,如果是正值,就代表新生成的内存多,释放的内存少。其中的闭包项,如果是正值,就说明存在内存泄漏。

下面我们到代码里找一个内存泄漏的问题:

Performance monitor

Chrome64 新增的一个 Performance monitor 可以帮助我们实时监听 performance 的变化情况。一般而言,浏览器的垃圾收集会每个一段时间运行一次,届时会释放大量资源,我们能在 Performance monitor 的折线图上看到折线断崖式下跌。一边操作页面,一边观察资源的变化可以帮助我们更快地定位问题。

哪些应用更容易内存泄漏

单页应用容易出现这个问题。

如果是普通网站,切换菜单页面就刷新了,所有内存会被回收,进行再分配,不太会出现严重的内存泄漏问题。

总结

最后,关于内存泄漏,我发现几乎所有网站都存在这个问题,并且有很多的内存泄漏,都是第三方库和框架的打包文件带来的。所以说,对于内存泄漏,只要不是特别严重,开发者是没有单独处理的必要的。

然而我司的应用就另当别论了,,

别问我有多少内存泄漏。

五十三万个,我日。。