什么是内存泄露?
内存泄露是指new了一块内存,但无法被释放或者被垃圾回收。new了一个对象之后,它申请占用了一块堆内存,当把这个对象指针置为null时或者离开作用域导致被销毁,那么这块内存没有人引用它了在JS里面就会被自动垃圾回收。但是如果这个对象指针没有被置为null,且代码里面没办法再获取到这个对象指针了,就会导致无法释放掉它指向的内存,也就是说发生了内存泄露。
Chrome dev tool 术语
Shallow size
这是指对象本身获得的内存大小。
典型的 JavaScript 对象会获得一些保留的内存,用于他们的描述以及存储即时产生的值。通常情况下,只有数组和字符串才会有比较明显的浅层大小。不过,字符串和外部数组往往在渲染内存中有它们自己的主存储器,对 JavaScript 堆只露出一点包装后的对象。
渲染内存是指所监视的页面被渲染的过程中使用的内存:原本分配的内存 + 该页面在 JS 堆中的内存 + 所有因为该页面而导致的 JS 堆中其他对象的内存开销。然而,即使是一个小的对象也可以通过阻止垃圾回收器自动回收其他对象来间接保有大量的内存。
Retained size
这是指对象以及其相关的对象一起被删除后所释放的内存大小,并且 GC roots 无法到达该处。
GC roots 是由在从原生代码的 V8 之外引用 JavaScript 对象的时候所创建的句柄(局部或者全局的)构成的。这些句柄可以再堆的快照中 GC roots > Handle scope 以及 GC roots > Global handles 中找到。在没有谈及浏览器实现的细节的情况下,就在本文中说明句柄会令读者感到困惑,故而关于句柄的细节本文不做讲解。事实上,无论 GC roots 还是句柄,都不是你需要担心的东西。
使用 Chrome 任务管理器实时监视内存使用
使用 Chrome 任务管理器作为内存问题调查的起点。 任务管理器是一个实时监视器,可以告诉您页面当前正在使用的内存量。
1、按 Shift+Esc 或者转到 Chrome 主菜单并选择 More tools > Task manager,打开任务管理器。
2、右键点击任务管理器的表格标题并启用 JavaScript memory。
下面两列可以告诉您与页面的内存使用有关的不同信息:
- Memory 列表示原生内存。DOM 节点存储在原生内存中。 如果此值正在增大,则说明正在创建 DOM 节点。
- JavaScript Memory 列表示 JS 堆。此列包含两个值。 您感兴趣的值是实时数字(括号中的数字)。 实时数字表示您的页面上的可到达对象正在使用的内存量。 如果此数字在增大,要么是正在创建新对象,要么是现有对象正在增长。
使用堆快照发现已分离 DOM 树的内存泄漏
只有页面的 DOM 树或 JavaScript 代码不再引用 DOM 节点时,DOM 节点才会被作为垃圾进行回收。 如果某个节点已从 DOM 树移除,但某些 JavaScript 仍然引用它,我们称此节点为“已分离”。已分离的 DOM 节点是内存泄漏的常见原因。此部分将教您如何使用 DevTools 的堆分析器确定已分离的节点。
堆快照是确定已分离节点的一种方式。顾名思义,堆快照可以为您显示拍摄快照时内存在您页面的 JS 对象和 DOM 节点间的分配。
要创建快照,请打开 DevTools 并转到 Memory面板,选择 Heap Snapshot 单选按钮,然后按 Take Snapshot 按钮
快照可能需要一些时间处理和加载。完成后,请从左侧面板(名称为 HEAP SNAPSHOTS)中选择该快照。
在 Class filter 文本框中键入 Detached,搜索已分离的 DOM 树。
展开三角符号以调查分离的树。
使用分配时间线确定 JS 堆内存泄漏
分配时间线是您可以用于跟踪 JS 堆中内存泄漏的另一种工具。
要显示分配时间线,请考虑使用下面的代码
var x = [];
function grow() {
x.push(new Array(1000000).join('x'));
}
document.getElementById('grow').addEventListener('click', grow);
每次按代码中引用的按钮时,都会向 x
数组添加一个由 100 万个字符组成的字符串。
要记录分配时间线,请打开 DevTools,然后转到 Memory 面板,选择 Allocation instrumentation on timeline 单选按钮,按 Start 按钮,执行您怀疑导致内存泄漏的操作。完成后,按 stop recording 按钮 。
记录时,请注意分配时间线上是否显示任何蓝色竖线(如下面的屏幕截图所示)。
这些蓝色竖线表示新内存分配。新内存分配中可能存在内存泄漏。 您可以在竖线上放大,将 Constructor 窗格筛选为仅显示在指定时间范围内分配的对象。
展开对象并点击它的值,可以在 Object 窗格中查看其更多详情。
造成内存泄露的可能会有以下几种情况:
(1)监听在window/body等事件没有解绑
(2)绑在EventBus的事件没有解绑
(3)Vuex的$store watch了之后没有unwatch
(4)模块形成的闭包内部变量使用完后没有置成null
(5)使用第三方库创建,没有调用正确的销毁函数
(6)被遗忘的计时器或回调函数
参考链接
一个Vue页面的内存泄露分析
Chrome开发工具 JavaScript 内存分析
chrome内存泄露(一)、内存泄漏分析工具
网友评论