了解如何使用 Chrome 和 DevTools 查找影响页面性能的内存问题,包括内存泄漏、内存膨胀和频繁的垃圾回收。javascript
在 RAIL 性能模型的精髓中,您的性能工做的焦点应是用户。java
内存问题相当重要,由于这些问题常常会被用户察觉。 用户可经过如下方式察觉内存问题:web
内存泄漏很容易肯定。若是网站使用的内存愈来愈多,则说明发生内存泄漏。 但内存膨胀比较难以界定。 什么状况才算是“使用过多的内存”?chrome
这里不存在硬性数字,由于不一样的设备和浏览器具备不一样的能力。 在高端智能手机上流畅运行的相同页面在低端智能手机上则可能崩溃。数组
界定的关键是使用 RAIL 模型并以用户为中心。了解什么设备在您的用户中深受欢迎,而后在这些设备上测试您的页面。若是体验一直糟糕,则页面可能超出这些设备的内存能力。浏览器
使用 Chrome 任务管理器做为内存问题调查的起点。 任务管理器是一个实时监视器,能够告诉您页面当前正在使用的内存量。app
按 Shift+Esc 或者转到 Chrome 主菜单并选择 More tools > Task manager,打开任务管理器。dom
右键点击任务管理器的表格标题并启用 JavaScript memory。 ide
下面两列能够告诉您与页面的内存使用有关的不一样信息:chrome-devtools
您也可使用 Timeline 面板做为调查的起点。 Timeline 面板能够帮助您直观了解页面在一段时间内的内存使用状况。
提示:一种比较好的作法是使用强制垃圾回收开始和结束记录。 在记录时点击 Collect garbage 按钮 () 能够强制进行垃圾回收。
要显示 Timeline 内存记录,请考虑使用下面的代码:
var x = []; function grow() { for (var i = 0; i < 10000; i++) { document.body.appendChild(document.createElement('div')); } x.push(new Array(1000000).join('x')); } document.getElementById('grow').addEventListener('click', grow);
每次按代码中引用的按钮时,将向文档正文附加 1 万个 div
节点,并将一个由 100 万个 x
字符组成的字符串推送到 x
数组。运行此代码会生成一个相似于如下屏幕截图的 Timeline 记录:
首先,咱们来讲明一下用户界面。Overview 窗格中的 HEAP 图表(NET 下方)表示 JS 堆。概览窗格下方是计数器窗格。从这里,您能够看到内存使用按 JS 堆 (与 Overview 窗格中的 HEAP 图表相同)、文档、DOM 节点、侦听器和 GPU 内存细分。停用对应的复选框能够将其在图表中隐藏。
如今,咱们将根据屏幕截图来分析代码。若是查看节点计数器(绿色图表),您会看到它与代码彻底匹配。节点计数以离散步长方式增大。 您能够假定节点计数的每次增大都是对 grow()
的一次调用。 JS 堆图表(蓝色图表)的显示并不直接。为了符合最佳作法,第一次降低其实是一次强制垃圾回收(经过按 Collect garbage 按钮实现)。随着记录的进行,您会看到 JS 堆大小高低交错变化。这种现象是正常的而且在预料之中:每次点击按钮,JavaScript 代码都会建立 DOM 节点,在建立由 100 万个字符组成的字符串期间,代码会完成大量工做。这里的关键是,JS 堆在结束时会比开始时大(这里“开始”是指强制垃圾回收后的时间点)。在实际使用过程当中,若是您看到这种 JS 堆大小或节点大小不断增大的模式,则可能存在内存泄漏。
只有页面的 DOM 树或 JavaScript 代码再也不引用 DOM 节点时,DOM 节点才会被做为垃圾进行回收。 若是某个节点已从 DOM 树移除,但某些 JavaScript 仍然引用它,咱们称此节点为“已分离”。已分离的 DOM 节点是内存泄漏的常见缘由。此部分将教您如何使用 DevTools 的堆分析器肯定已分离的节点。
下面是一个已分离 DOM 节点的简单示例。
var detachedNodes; function create() { var ul = document.createElement('ul'); for (var i = 0; i < 10; i++) { var li = document.createElement('li'); ul.appendChild(li); } detachedTree = ul; } document.getElementById('create').addEventListener('click', create);
点击代码中引用的按钮将建立一个包含 10 个 li
子级的 ul
节点。 这些节点由代码引用,但不存在于 DOM 树中,所以它们已分离。
堆快照是肯定已分离节点的一种方式。顾名思义,堆快照能够为您显示拍摄快照时内存在您页面的 JS 对象和 DOM 节点间的分配。
要建立快照,请打开 DevTools 并转到 Profiles 面板,选择 Take Heap Snapshot 单选按钮,而后按 Take Snapshot 按钮。
快照可能须要一些时间处理和加载。完成后,请从左侧面板(名称为 HEAP SNAPSHOTS)中选择该快照。
在 Class filter 文本框中键入 Detached
,搜索已分离的 DOM 树。
以黄色突出显示的节点具备 JavaScript 代码对它们的直接引用。 以红色突出显示的节点则没有直接引用。只有属于黄色节点的树时,它们才处于活动状态。 通常而言,您须要将注意力放在黄色节点上。 修复代码,使黄色节点处于活动状态的时间不长于须要的时间,您也须要消除属于黄色节点树的红色节点。
点击黄色节点对其进行进一步调查。在 Object 窗格中,您能够看到与正在引用该节点的代码相关的更多信息。 例如,在下面的屏幕截图中,您能够看到 detachedTree
变量正在引用该节点。要解决这一特定的内存泄漏,您须要研究使用 detachedTree
的代码并确保在不须要时,此代码能够移除其对节点的引用。
分配时间线是您能够用于跟踪 JS 堆中内存泄漏的另外一种工具。
要显示分配时间线,请考虑使用下面的代码:
var x =[];
function grow(){
x.push(newArray(1000000).join('x'));
}
document.getElementById('grow').addEventListener('click', grow);
每次按代码中引用的按钮时,都会向 x
数组添加一个由 100 万个字符组成的字符串。
要记录分配时间线,请打开 DevTools,而后转到 Profiles 面板,选择 Record Allocation Timeline 单选按钮,按 Start 按钮,执行您怀疑致使内存泄漏的操做。完成后,按 stop recording 按钮 ()。
记录时,请注意分配时间线上是否显示任何蓝色竖线(以下面的屏幕截图所示)。
这些蓝色竖线表示新内存分配。新内存分配中可能存在内存泄漏。 您能够在竖线上放大,将 Constructor 窗格筛选为仅显示在指定时间范围内分配的对象。
展开对象并点击它的值,能够在 Object 窗格中查看其更多详情。 例如,在下面的屏幕截图中,经过查看新分配对象的详细信息,您能够看到它被分配到 Window
做用域中的 x
变量。
使用 Record Allocation Profiler 类型可按 JavaScript 函数查看内存分配。
DevTools 按函数显示内存分配明细。默认视图为 Heavy (Bottom Up),将分配了最多内存的函数显示在最上方。
若是感受页面常常暂停,则可能存在垃圾回收问题。
您可使用 Chrome 任务管理器或者 Timeline 内存记录发现频繁的垃圾回收。 在任务管理器中,Memory 或 JavaScript Memory 值频繁上升和降低表示存在频繁的垃圾回收。在 Timeline 记录中,JS 堆或节点计数图表频繁上升和降低指示存在频繁的垃圾回收。
肯定问题后,您可使用分配时间线记录找出内存正在分配到什么地方,以及哪些函数致使分配。