修复内存问题Fix memory problems

了解如何使用 Microsoft Edge 和 DevTools 查找影响页面性能的内存问题,包括内存泄漏、内存不足和频繁垃圾回收。Learn how to use Microsoft Edge and DevTools to find memory issues that affect page performance, including memory leaks, memory bloat, and frequent garbage collections.

摘要Summary

  • 了解你的页面当前与浏览器任务管理器Microsoft Edge的内存量。Find out how much memory your page is currently using with the Microsoft Edge Browser Task Manager.
  • 使用内存面板直观呈现一段时间 的内存 使用情况。Visualize memory usage over time with the Memory panel.
  • 使用堆快照 (分离的 DOM 树 ) 内存泄漏 的常见原因Identify detached DOM trees (a common cause of memory leaks) with Heap snapshot.
  • 在时间线上通过 Allocation instrumentation 了解 JavaScript 堆 \ (JS 堆) 分配新内存的时间Find out when new memory is being allocated in your JavaScript heap (JS heap) with Allocation instrumentation on timeline.

概述Overview

在 RAIL 性能模型 方面,性能工作的重点应该是用户。In the spirit of the RAIL performance model, the focus of your performance efforts should be your users.

内存问题非常重要,因为它们经常被用户感知。Memory issues are important because they are often perceivable by users. 用户可能通过以下方式发现内存问题:Users may perceive memory issues in the following ways:

  • 随着时间的推移,页面的性能逐渐变差The performance of a page gets progressively worse over time. 这可能是内存泄漏症状。This is possibly a symptom of a memory leak. 内存泄漏是页面中的 Bug 导致页面随着时间的推移逐渐使用更多内存。A memory leak is when a bug in the page causes the page to progressively use more and more memory over time.
  • 页面的性能一直不佳The performance of a page is consistently bad. 这可能是内存不足现象。This is possibly a symptom of memory bloat. 当页面使用的内存多于最佳页面速度所需的内存时,内存量会变大。Memory bloat is when a page uses more memory than is necessary for optimal page speed.
  • 页面的性能会延迟或看似频繁暂停The performance of a page is delayed or appears to pause frequently. 这可能是频繁垃圾回收症状。This is possibly a symptom of frequent garbage collections. 垃圾回收是在浏览器回收内存时进行。Garbage collection is when the browser reclaims memory. 浏览器决定何时发生这种情况。The browser decides when this happens. 在集合期间,所有正在运行的脚本将暂停。During collections, all script running is paused. 因此,如果浏览器大量进行垃圾回收,脚本运行时将暂停很多。So if the browser is garbage collecting a lot, script runtime is going to get paused a lot.

内存过多:"太多"多少?Memory bloat: how much is "too much"?

内存泄漏易于定义。A memory leak is easy to define. 如果网站逐渐使用更多内存,则有泄漏。If a site is progressively using more and more memory, then you have a leak. 但是,内存不足有点难以固定。But memory bloat is a bit harder to pin down. 什么限定为"使用过多的内存"?What qualifies as "using too much memory"?

此处没有硬数,因为不同的设备和浏览器具有不同的功能。There are no hard numbers here, because different devices and browsers have different capabilities. 在高端智能手机上流畅运行的同一页面可能在低端智能手机上崩溃。The same page that runs smoothly on a high-end smartphone may crash on a low-end smartphone.

这里的关键是使用 RAIL 模型,并专注于你的用户。The key here is to use the RAIL model and focus on your users. 了解哪些设备受你的用户欢迎,然后在这些设备上测试你的页面。Find out what devices are popular with your users, and then test out your page on those devices. 如果体验始终不佳,则页面可能会超出这些设备的内存功能。If the experience is consistently bad, the page may be exceeding the memory capabilities of those devices.

使用浏览器任务管理器实时Microsoft Edge内存使用Monitor memory use in realtime with the Microsoft Edge Browser Task Manager

使用Microsoft Edge浏览器任务管理器作为内存问题调查的起点。Use the Microsoft Edge Browser Task Manager as a starting point to your memory issue investigation. 浏览器Microsoft Edge管理器是实时监视器,可告诉你页面当前使用的内存量。The Microsoft Edge Browser Task Manager is a realtime monitor that tells you how much memory a page is currently using.

  1. 选择 Shift + Esc 或导航到****"Microsoft Edge"主菜单,然后选择"更多工具""浏览器任务管理器"以Microsoft Edge > **** 浏览器任务管理器"。Select Shift+Esc or navigate to the Microsoft Edge main menu and choose More tools > Browser Task Manager to open the Microsoft Edge Browser Task Manager.

    打开Microsoft Edge浏览器任务管理器

  2. 将鼠标悬停在浏览器任务管理器Microsoft Edge表标题上,打开上下文菜单 \ (右键单击) ,并启用JavaScript 内存Hover on the table header of the Microsoft Edge Browser Task Manager, open the contextual menu (right-click), and enable JavaScript memory.

    启用 JavaScript 内存

这两列告诉您有关页面如何使用内存的不同内容。These two columns tell you different things about how your page is using memory.

  • " 内存 "列表示本机内存。The Memory column represents native memory. DOM 节点存储在本机内存中。DOM nodes are stored in native memory. 如果此值增加,将创建 DOM 节点。If this value is increasing, DOM nodes are getting created.
  • "JavaScript 内存" 列表示 JS 堆。The JavaScript Memory column represents the JS heap. 此列包含两个值。This column contains two values. 您感兴趣的值为活动号码 \ (括号) 。The value you are interested in is the live number (the number in parentheses). 实时数表示页面上的可访问对象使用的内存量。The live number represents how much memory the reachable objects on your page are using. 如果此数目增加,则要么正在创建新对象,要么现有对象正在增长。If this number is increasing, either new objects are being created, or the existing objects are growing.

使用性能面板可视化内存泄漏Visualize memory leaks with Performance panel

您还可以使用性能面板作为调查的另一个起点。You may also use the Performance panel as another starting point in your investigation. "性能"面板可帮助您直观呈现页面在一段时间的内存使用。The Performance panel helps you visualize the memory use of a page over time.

  1. 打开**** DevTools 上的"性能"面板。Open the Performance panel on DevTools.
  2. 启用" 内存" 复选框。Enable the Memory checkbox.
  3. 录制Make a recording.

提示

最佳做法是使用强制垃圾回收开始和结束录制。It is a good practice to start and end your recording with a forced garbage collection. 若要强制进行垃圾回收,请选择记录时 收集垃圾回收  强制垃圾回收按钮。To force garbage collection, choose the collect garbage force garbage collection button while recording.

若要演示内存录制,请考虑以下代码:To demonstrate memory recordings, consider the code below:

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);

每次选择代码中引用的按钮时,都会将一万个节点追加到文档正文,并且一个包含一百万个字符的字符串 div x 被推送到 x 数组。Every time that the button referenced in the code is chosen, ten thousand div nodes are appended to the document body, and a string of one million x characters is pushed onto the x array. 运行前面的代码示例在性能面板中 生成 记录,如下图所示。Running the previous code sample produces a recording in the Performance panel like the following figure.

简单增长

首先,用户界面的说明。First, an explanation of the user interface. "概述"窗格**** \ (中的 HEAP 图) 表示JS 堆。The HEAP graph in the Overview pane (below NET) represents the JS heap. "概述 "窗格 下方是" 计数器" 窗格。Below the Overview pane is the Counter pane. 内存使用率由 JS 堆 \ (与概述窗格) 、文档、DOM**** 节点、侦听器和 GPU 内存中的HEAP图形相同。The memory usage is broken down by JS heap (same as HEAP graph in the Overview pane), documents, DOM nodes, listeners, and GPU memory. 关闭复选框以在图形中隐藏它。Turn off a checkbox to hide it from the graph.

现在,代码分析与上图比较。Now, an analysis of the code compared with the previous figure. 如果查看节点计数器 \ (绿色图形) ,它将与代码完全匹配。If you review the node counter (the green graph), it matches up cleanly with the code. 节点计数在离散步骤中增加。The node count increases in discrete steps. 您可能认为每次增加的节点数都是对 的调用 grow()You may presume that each increase in the node count is a call to grow(). JS 堆图 \ (蓝图) 不是那么简单。The JS heap graph (the blue graph) is not as straightforward. 为了与最佳做法保持一样,第一个下降实际上是强制垃圾回收 \ (选择 收集垃圾回收  强制垃圾回收按钮 ) 。In keeping with best practices, the first dip is actually a forced garbage collection (choose the collect garbage force garbage collection button). 在记录进行时,将显示 JS 堆大小峰值。As the recording progresses, the JS heap size spikes are displayed. 这是自然且预期的:JavaScript 代码将在你选择的每一个按钮上创建 DOM 节点,并创建一百万个字符的字符串时执行大量工作。This is natural and expected: the JavaScript code is creating the DOM nodes on every button you choose and doing a lot of work when it creates the string of one million characters. 此处的关键点是 JS 堆结束时间高于其开头 \ ("开头"是强制垃圾回收) 。The key thing here is the fact that the JS heap ends higher than it began (the "beginning" here being the point after the forced garbage collection). 在现实世界中,如果你看到这种增加 JS 堆大小或节点大小的模式,它可能会定义内存泄漏。In the real world, if you saw this pattern of increasing JS heap size or node size, it may potentially define a memory leak.

使用堆快照发现分离的 DOM 树内存泄漏Discover detached DOM tree memory leaks with Heap Snapshots

DOM 节点仅在没有从页面上运行的 DOM 树或 JavaScript 代码引用该节点时进行垃圾回收。A DOM node is only garbage collected when there are no references to the node from either the DOM tree or JavaScript code running on the page. 当从 DOM 树中删除节点时,该节点将被"分离",但某些 JavaScript 仍引用它。A node is said to be "detached" when it is removed from the DOM tree but some JavaScript still references it. 分离的 DOM 节点是内存泄漏的常见原因。Detached DOM nodes are a common cause of memory leaks. 本部分指导你如何使用 DevTools 中的堆探查器来确定分离的节点。This section teaches you how to use the heap profilers in DevTools to identify detached nodes.

下面是分离 DOM 节点的简单示例。Here is a simple example of detached DOM nodes.

var detachedTree;

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);

选择代码中引用的按钮将创建一 ul 个包含 10 个子节点 li 的节点。Choosing the button referenced in the code creates a ul node with ten li children. 节点由代码引用,但在 DOM 树中不存在,因此会分离每个节点。The nodes are referenced by the code but do not exist in the DOM tree, so each is detached.

堆快照是标识分离节点的一种方法。Heap snapshots are one way to identify detached nodes. 正如该名称所示,堆快照显示了在快照时间点如何在页面的 JS 对象和 DOM 节点之间分配内存。As the name implies, heap snapshots show you how memory is distributed among the JS objects and DOM nodes for your page at the point of time of the snapshot.

若要创建快照,请打开 DevTools 并导航**** 到内存面板,选择**** 堆快照单选按钮>拍摄快照按钮。To create a snapshot, open DevTools and navigate to the Memory panel, choose the Heap snapshot radio button > Take snapshot button.

拍摄堆快照

可能需要一些时间处理和加载快照。The snapshot may take some time to process and load. 完成后,从名为 HEAP SNAPSHOTS\ (的左侧面板 ) 。After it is finished, select it from the left-hand panel (named HEAP SNAPSHOTS).

Detached筛选器文本框中 键入以搜索分离的 DOM 树。Type Detached in the Class filter textbox to search for detached DOM trees.

筛选分离的节点

展开树状以调查分离的树。Expand the carats to investigate a detached tree.

调查分离的树

选择一个节点以进一步调查它。Choose a node to investigate it further. "对象 "窗格中,您可以查看有关引用它的代码的详细信息。In the Objects pane, you may review more information about the code that is referencing it. 例如,在下图中, detachedNodes 变量引用节点。For example, in the following figure, the detachedNodes variable is referencing the node. 若要修复特定内存泄漏,应研究使用 变量的代码,并确保在不再需要节点时删除 detachedUNode 对节点的引用。To fix the particular memory leak, you should study the code that uses the detachedUNode variable and ensure that the reference to the node is removed when it is no longer needed.

调查节点

在时间线上通过 Allocation instrumentation 识别 JS 堆内存泄漏Identify JS heap memory leaks with Allocation instrumentation on timeline

时间线上的分配检测 是另一个可以帮助你跟踪 JS 堆中的内存泄漏的工具。Allocation instrumentation on timeline is another tool that may help you track down memory leaks in your JS heap.

使用 下面的代码演示时间线 上的分配检测。Demonstrate Allocation instrumentation on timeline using the following code.

var x = [];
function grow() {
    x.push(new Array(1000000).join('x'));
}
document.getElementById('grow').addEventListener('click', grow);

每次推送代码中引用的按钮时,都会向数组中添加一个包含一百万个字符 x 的字符串。Every time that the button referenced in the code is pushed, a string of one million characters is added to the x array.

若要在时间线上记录分配检测,请打开 DevTools,导航**** 到内存面板,选择时间线上的分配检测单选按钮,选择****"开始"按钮,执行你怀疑导致内存泄漏的操作,然后在完成后选择停止录制堆配置文件停止录制****  按钮。To record an Allocation instrumentation on timeline, open DevTools, navigate to the Memory panel, choose the Allocation instrumentation on timeline radio button, choose the Start button, perform the action that you suspect is causing the memory leak, and then choose the Stop recording heap profile stop recording button when you are done.

在录制时,请注意时间线上的 Allocation instrumentation 上是否显示任何蓝色条,如下图所示。As you are recording, notice if any blue bars show up on the Allocation instrumentation on timeline, like in the following figure.

新分配

这些蓝色条代表新的内存分配。Those blue bars represent new memory allocations. 这些新的内存分配是内存泄漏的候选项。Those new memory allocations are your candidates for memory leaks. 可以缩放栏以筛选"构造函数"窗格,以**** 只显示指定时间范围内分配的对象。You are able to zoom on a bar to filter the Constructor pane to only show objects that were allocated during the specified timeframe.

已缩放的分配时间线

展开对象并选择值以查看"对象"窗格中的 更多详细信息Expand the object and select the value to view more details in the Object pane. 例如,下图中新分配的对象的详细信息指示已分配给范围 x 中的 Window 变量。For example, in the following figure, in the details of the newly allocated object indicates that it was allocated to the x variable in the Window scope.

对象详细信息

按功能调查内存分配Investigate memory allocation by function

使用 分配采样 分析类型查看 JavaScript 函数的内存分配。Use the Allocation sampling profiling type to view memory allocation by JavaScript function.

记录分配采样

  1. 选择“分配采样”按钮。Choose the Allocation sampling radio button. 如果页面上有工作线程,可以使用"开始"按钮旁边的下拉菜单选择该工作 线程作为分析 目标。If there is a worker on the page, you are able to select that as the profiling target using the dropdown menu next to the Start button.
  2. 选择" 开始" 按钮。Choose the Start button.
  3. 完成要调查的网页上的操作。Complete the actions on the webpage which you want to investigate.
  4. 完成 所有操作 后,选择"停止"按钮。Choose the Stop button when you have finished all of your actions.

DevTools 显示按功能分配内存的细目。DevTools shows you a breakdown of memory allocation by function. 默认视图为 **"高 (从 **下) ",它显示在顶部分配了大部分内存的函数。The default view is Heavy (Bottom Up), which displays the functions that allocated the most memory at the top.

分配采样

专色频繁垃圾回收Spot frequent garbage collections

如果页面似乎频繁暂停,则可能有垃圾回收问题。If your page appears to pause frequently, then you may have garbage collection issues.

可以使用 Microsoft Edge 浏览器任务管理器或性能内存记录来发现频繁垃圾回收。You are able to use either the Microsoft Edge Browser Task Manager or Performance memory recordings to spot frequent garbage collection. 在 Microsoft Edge 浏览器任务管理器中,经常出现和下降 的内存JavaScript 内存 值表示频繁垃圾回收。In the Microsoft Edge Browser Task Manager, frequently rising and falling Memory or JavaScript Memory values represent frequent garbage collection. 在性能记录中,频繁更改 \ (和下降) JS 堆或节点计数图指示频繁进行垃圾回收。In Performance recordings, frequent changes (rising and falling) to the JS heap or node count graphs indicate frequent garbage collection.

确定问题后,可以在时间线记录上使用 Allocation instrumentation 来查明内存的分配位置以及导致分配的函数。After you have identified the problem, you are able to use an Allocation instrumentation on timeline recording to find out where memory is being allocated and which functions are causing the allocations.

联系 Microsoft Edge DevTools 团队Getting in touch with the Microsoft Edge DevTools team

使用以下选项讨论帖子中的新功能和更改,或与 DevTools 相关的任何其他内容。Use the following options to discuss the new features and changes in the post, or anything else related to DevTools.

  • 使用“发送反馈”图标发送反馈,或在 DevTools 中选择Alt+Shift+I \ (Windows、Linux) 或 Option+Shift+I \ (macOS)。Send your feedback using the Send Feedback icon or select Alt+Shift+I (Windows, Linux) or Option+Shift+I (macOS) in DevTools.
  • 发推 @EdgeDevToolsTweet at @EdgeDevTools.
  • 我们想要的 Web 提交建议。Submit a suggestion to The Web We Want.
  • 若要提交有关本文的错误,请使用以下“反馈” 部分。To file bugs about this article, use the following Feedback section.

Microsoft Edge DevTools 中的“发送反馈”图标

备注

此页面的某些部分是根据 Google 创建和共享的作品所做的修改,并根据 Creative Commons Attribution 4.0 International License 中描述的条款使用。Portions of this page are modifications based on work created and shared by Google and used according to terms described in the Creative Commons Attribution 4.0 International License.
原始页面位于此处,由 Kayce Basques\(Chrome DevTools & Lighthouse 的技术作家\)撰写。The original page is found here and is authored by Kayce Basques (Technical Writer, Chrome DevTools & Lighthouse).

Creative Commons License
本作品根据 Creative Commons Attribution 4.0 International License 获得许可。This work is licensed under a Creative Commons Attribution 4.0 International License.