JS-内存泄漏定位

195 阅读4分钟

前言

以下案例是笔者在项目开发中真实遇见的情况,但是由于是公司项目,不方便截图,因此自己手动敲了一个demo用于演示。另外,本次演示,不会过多的阐述相关的概念,只演示定位问题的过程

准备

定位JS内存泄漏,一般用的是浏览器的 devtool 工具,首先可以用 Performance 面板,判断是否存在内存泄漏,然后使用 Memory 面板,定位发生的问题。

开始debug

首先,demo是以移动端为范例做的,接下来的操作只会发生在底部导航条,并且4个路由按顺序切换,如下图

image.png

如下图,打开浏览器 Performance面板,先点击1的,让浏览器进行强制垃圾回收,确保环境是“干净”的,然后点击2进行录制

image.png 录制开始后,按照顺序依次点击4个路由,首页=》消息=》购物车=》我的,轮回4次,结束录制,得到下图

image.png 从上图,可以看出,每一个轮回,JS内存都是一个上升趋势,最后回收下降的基准,也比开始的要高,这里只高了一丢丢,是因为本demo,只做了很小的内存泄漏,这是为了模拟真实的项目开发,内存泄漏就是就一点点累计而来的,而不是一下大爆发

这里插一句题外话,内存线逐步向上,并不能代表一定是内存泄漏,缓存也是会引起这样的现象,真实项目在操作这一步,可不是像现在这样,几个步骤就完事,而是需要一个完整且细致的操作,最后才能得到有效的展示图

当我们怀疑项目存在内存泄漏时,此时,需要定位是什么操作引起的,分析内存线,再结合顶上的操作路径,可以分析出,每次轮回,在“消息”和“购物车”这2个页面,都会引起JS内存线的上升

image.png

然后打开 Memory 面板,如下图,Memory 提供3种方式,笔者比较常用前2种,第一种是拍快照的形式,第二种根据时间线来查看内存的分配与回收,此时演示采取第二种

image.png

点击录制,这次不需要点击4个路由,上面我们已经定位到是“消息”和“购物车”两个页面可能存在问题,因此,此次只需要在这2个路由轮流切换,如下图,蓝色线条表示未被回收的内存(不代表就是内存泄漏),灰色表示被回收

image.png 选中第一处,如下图,会展示所有未被回收的内存信息

image.png 里面每个类都是很直白的翻译,比如第一个 compiled code,就是编译的代码,打开如下,当我们看到react-dom这样的字样,基本可以明白,这是react框架做的,可能会有人疑惑,怎么框架会出现这样的问题 ??? eum·····怎么说呢,适当的留下常用的全局变量,是有益程序的,这会帮助我们快速的调用api

image.png 在所有未被回收的类中,有一个闭包,显得格外刺眼,那就是(closure),打开如下,首先明确的告诉我们出现问题的文件和行数,还告诉我们,这个是一个DOMTimer,既定时器

image.png 打开上图告诉我们的文件地址和行数,如下:可以发现,原来是该组件初始化时,增加了个定时器,但是定时器却在挂载时没有被回收,好,第一个到此为止,first blood , get

image.png

接下来,再回到展示图,选择第二处,如下图,对比刚才第一张明显多了很多,这意味着要排查的地方就非常多

image.png

这里不再一个一个说明,直接上问题代码,如下,从图中能得到的信息是,有一个叫aa的函数,这里有个设置了监听器,但是监听器有没被回收

image.png 来到对应的代码处,修复即可

image.png

总结

相信很多人对第二处的排查是有疑虑,大概疑惑:Constructor下那多个,难道要每个点开去看嘛?

首先,笔者是在开发环境下去演示,而实际项目在调优的时候,肯定是在生产环境,要关闭浏览器插件,并开启无痕浏览,这个时候,会减少很多干扰的因素,但是,这并不意味着我们就能一下清晰的定位到问题。
笔者想传达的是,调优就是一个枯燥无味的过程,严格上讲,确实是要每个都点开看看,但是我们是能总结到一些经验的,比如看到 reatc 等明显的框架字眼,基本就不需要细看,又或者搜索 Detached 分析DOM分离节点等等,我们要利用自己的经验,去排除干扰因素,找到问题。