简介: 很多开发者可能平时并不关心自己维护的页面是否存在内存泄漏,原因可能是刚开始简单的页面内存泄漏的速度很缓慢,在造成严重卡顿之前可能就被用户刷新了,问题也就被隐藏了,但是随着页面越来越复杂,尤其当你的页面是 SAP 方式交互时,内存泄漏的隐患便越来越严重,直到突然有一天用户反馈说:“操作一会儿页面就卡住不动了,也不知道为什么,以前不这样的呀”。这篇文章通过一些简单的例子介绍内存泄漏的调查方法、总结内存泄漏出现的原因和常见情况,并针对每种情况总结如何避免内存泄漏。希望能对大家有所帮助。

作者 | 木及
来源 | 阿里技术公众号
很多开发者可能平时并不关心自己维护的页面是否存在内存泄漏,原因可能是刚开始简单的页面内存泄漏的速度很缓慢,在造成严重卡顿之前可能就被用户刷新了,问题也就被隐藏了,但是随着页面越来越复杂,尤其当你的页面是 SAP 方式交互时,内存泄漏的隐患便越来越严重,直到突然有一天用户反馈说:“操作一会儿页面就卡住不动了,也不知道为什么,以前不这样的呀”。
这篇文章通过一些简单的例子介绍内存泄漏的调查方法、总结内存泄漏出现的原因和常见情况,并针对每种情况总结如何避免内存泄漏。希望能对大家有所帮助。
一 一个简单的例子
先看一个简单的例子,下面是这个例子对应的代码:


代码 1
代码 1 的逻辑很简单:点击“add date”按钮时会向 dateAry 数组中 push 3000 个 new Date 对象,点击“clear”按钮时将 dateAry 清空。很明显,“add date”操作会造成内存占用不断增长,如果将这个逻辑用在实际应用中便会造成内存泄漏(不考虑故意将代码逻辑设计成这样的情况),下面我们看一下如何调查这种内存增长出现的原因以及如何找出内存泄漏点。
1 heap snapshot
为了避免浏览器插件的干扰,我们在 chrome 中新建一个无痕窗口打开上述代码。然后在 chrome 的 devtools 中的 Memory 工具中找到 “Heap Snapshot”工具,点击左上角的录制按钮录制一个 Snapshot,然后点击“add date”按钮,在手动触发 GC(Garbage Collect)之后,再次录制一个 Snapshot,反复执行上述操作若干次,像图 1 中操作的那样,得到一系列的 Snapshot。

图 1 录制 Snapshot
图 2 是我们刚刚得到的 Snapshot 组,其中的第一个是页面初始加载的时候录制的,不难发现,从第二个开始,每个 Snapshot 相比于上一个其大小都增加了约 200KB,我们点击选择 Snapshot 2,在 class filter 输入框中处输入 date,可以得到 Snapshot 2 中所有被 Date 构造器构造出来的 JS 对象,也就是 Date 对象,这里看到的构造器跟浏览器内部的实现有关,不必跟 JS 的对象对应。
选中一个 Date 对象,在下面的面板中可以看到所选对象的持有链以及相关持有对象的内存的保留大小(Retained Size),从图中可以看出选中的 Date 对象是 Array 的第 1 个元素(index 从 0 开始),而这个 Array 的持有者是 system/Context 上下文中的 dateAry,system/Context 上下文就是代码中 script 标签的上下文,我们可以看到在这个 dataAry 的保留大小是 197KB,我们再切到 Snapshot 3,用相同的方式查看内存持有和大小,可以发现 Snapshot 3 中的 dataAry 的保留大小变成了 386KB,相比于 Snapshot 2 增涨了约 200KB!逐一比较后面的 Snapshot 4、5 后也能得到相同的对比结果,即下一个 Snapshot 中的 dateAry 比上一个的保留大小大约 200KB。

图 2 录制的 Snapshot 组
参考【代码 1】我们可以知道,“add date”按钮在被点击时,会向 dateAry 数组中 push 3000 个新的 Date 对象,而在图 2 中的 Date 构造器的右侧可以看到这 3000 个 Date 对象(Date x 3000),它对应的正式我们的循环创建的那 3000 个 Date 对象。综合上面的操作我们可以知道,chorome devtools 中的 Memroy 的 Heap Snapshot 工具可以录制某一个时刻的所有内存对象,也就是一个“快照”,快照中按“构造器”分组,展示了所有被记录下来的 JS 对象。
最低0.47元/天 解锁文章
1541

被折叠的 条评论
为什么被折叠?



