使用堆快照

¥Using Heap Snapshot

你可以从正在运行的应用中获取堆快照并将其加载到 Chrome 开发者工具 中以检查某些变量或检查保留器大小。你也可以比较多个快照以查看随时间变化的差异。

¥You can take a Heap Snapshot from your running application and load it into Chrome Developer Tools to inspect certain variables or check retainer size. You can also compare multiple snapshots to see differences over time.

警告

¥Warning

创建快照时,主线程中的所有其他工作都将停止。根据堆内容,它甚至可能需要一分钟以上的时间。快照是在内存中构建的,因此它可以使堆大小加倍,从而填满整个内存,然后导致应用崩溃。

¥When creating a snapshot, all other work in your main thread is stopped. Depending on the heap contents it could even take more than a minute. The snapshot is built in memory, so it can double the heap size, resulting in filling up entire memory and then crashing the app.

如果你要在生产中获取堆快照,请确保你从中获取快照的进程可以崩溃而不会影响应用的可用性。

¥If you're going to take a heap snapshot in production, make sure the process you're taking it from can crash without impacting your application's availability.

如何

¥How To

获取堆快照

¥Get the Heap Snapshot

有多种方法可以获取堆快照:

¥There are multiple ways to obtain a heap snapshot:

  1. 通过检查器,

    ¥via the inspector,

  2. 通过外部信号和命令行标志,

    ¥via an external signal and command-line flag,

  3. 通过进程内的 writeHeapSnapshot 调用,

    ¥via a writeHeapSnapshot call within the process,

  4. 通过检查器协议。

    ¥via the inspector protocol.

1. 在检查器中使用内存分析

¥ Use memory profiling in inspector

适用于所有积极维护的 Node.js 版本

¥Works in all actively maintained versions of Node.js

使用 --inspect 标志运行 node 并打开检查器。

¥Run node with --inspect flag and open the inspector.

open inspector

获取堆快照的最简单方法是将检查器连接到本地运行的进程。然后转到“内存”选项卡并获取堆快照。

¥The simplest way to get a Heap Snapshot is to connect a inspector to your process running locally. Then go to Memory tab and take a heap snapshot.

take a heap snapshot

2. 使用 --heapsnapshot-signal 标志

¥ Use --heapsnapshot-signal flag

适用于 v12.0.0 或更高版本

¥Works in v12.0.0 or later

你可以使用命令行标志启动 node,以便对信号做出反应以创建堆快照。

¥You can start node with a command-line flag enabling reacting to a signal to create a heap snapshot.

$ node --heapsnapshot-signal=SIGUSR2 index.js

有关详细信息,请参阅 heapsnapshot-signal 标志 的最新文档。

¥For details, see the latest documentation of heapsnapshot-signal flag.

3. 使用 writeHeapSnapshot 功能

¥ Use writeHeapSnapshot function

适用于 v11.13.0 或更高版本 可以在带有 heapdump 包 的旧版本中工作

¥Works in v11.13.0 or later Can work in older versions with heapdump package

如果你需要来自工作进程的快照,例如在服务器上运行的应用,你可以使用以下方式实现获取它:

¥If you need a snapshot from a working process, like an application running on a server, you can implement getting it using:

require('v8').writeHeapSnapshot();

检查 writeHeapSnapshot 文档 的文件名选项。

¥Check writeHeapSnapshot docs for file name options.

你需要有一种在不停止进程的情况下调用它的方法,因此建议在 HTTP 处理程序中调用它或作为对操作系统信号的响应。注意不要暴露触发快照的 HTTP 端点。其他任何人都不可能访问它。

¥You need to have a way to invoke it without stopping the process, so calling it in a HTTP handler or as a reaction to a signal from the operating system is advised. Be careful not to expose the HTTP endpoint triggering a snapshot. It should not be possible for anybody else to access it.

对于 v11.13.0 之前的 Node.js 版本,你可以使用 heapdump 包

¥For versions of Node.js before v11.13.0 you can use the heapdump package.

4. 使用检查器协议触发堆快照

¥ Trigger Heap Snapshot using inspector protocol

Inspector 协议可用于从进程外部触发堆快照。

¥Inspector protocol can be used to trigger Heap Snapshot from outside of the process.

无需运行 Chromium 的实际检查器即可使用 API。

¥It's not necessary to run the actual inspector from Chromium to use the API.

以下是使用 websocat 和可读流的 Bash 快照触发器示例,使用 websocatjq

¥Here's an example snapshot trigger in bash, using websocat and jq:

#!/bin/bash
set -e

kill -USR1 "$1"
rm -f fifo out
mkfifo ./fifo
websocat -B 10000000000 "$(curl -s http://localhost:9229/json | jq -r '.[0].webSocketDebuggerUrl')" < ./fifo > ./out &
exec 3>./fifo
echo '{"method": "HeapProfiler.enable", "id": 1}' > ./fifo
echo '{"method": "HeapProfiler.takeHeapSnapshot", "id": 2}' > ./fifo
while jq -e "[.id != 2, .result != {}] | all" < <(tail -n 1 ./out); do
  sleep 1s
  echo "Capturing Heap Snapshot..."
done

echo -n "" > ./out.heapsnapshot
while read -r line; do
  f="$(echo "$line" | jq -r '.params.chunk')"
  echo -n "$f" >> out.heapsnapshot
  i=$((i+1))
done < <(cat out | tail -n +2 | head -n -1)

exec 3>&-

以下是可与检查器协议一起使用的内存分析工具的非详尽列表:

¥Here is a non-exhaustive list of memory profiling tools usable with the inspector protocol:

如何使用堆快照查找内存泄漏

¥How to find a memory leak with Heap Snapshots

你可以通过比较两个快照来发现内存泄漏。确保快照差异不包含不必要的信息非常重要。以下步骤应在快照之间产生清晰的差异。

¥You can find a memory leak by comparing two snapshots. It's important to make sure the snapshots difference does not contain unnecessary information. Following steps should produce a clean diff between snapshots.

  1. 让进程加载所有源并完成引导。最多需要几秒钟。

    ¥Let the process load all sources and finish bootstrapping. It should take a few seconds at most.

  2. 开始使用你怀疑泄漏内存的功能。它可能会进行一些初始分配,但这些分配不是泄漏的分配。

    ¥Start using the functionality you suspect of leaking memory. It's likely it makes some initial allocations that are not the leaking ones.

  3. 拍一张堆快照。

    ¥Take one heap snapshot.

  4. 继续使用该功能一段时间,最好不要在其间运行任何其他程序。

    ¥Continue using the functionality for a while, preferably without running anything else in between.

  5. 再拍一张堆快照。两者之间的区别应该主要包含泄漏的内容。

    ¥Take another heap snapshot. The difference between the two should mostly contain what was leaking.

  6. 打开 Chromium/Chrome 开发工具并转到“内存”选项卡

    ¥Open Chromium/Chrome dev tools and go to Memory tab

  7. 首先加载较旧的快照文件,然后加载较新的快照文件。

    ¥Load the older snapshot file first, and the newer one second.

Load button in tools

  1. 选择较新的快照,并在顶部的下拉菜单中将模式从“摘要”切换到“比较”。

    ¥Select the newer snapshot and switch mode in the dropdown at the top from Summary to Comparison.

Comparison dropdown

  1. 查找较大的正增量并在底部面板中探索导致它们的引用。

    ¥Look for large positive deltas and explore the references that caused them in the bottom panel.

你可以练习使用 此堆快照练习 捕获堆快照和查找内存泄漏。

¥You can practice capturing heap snapshots and finding memory leaks with this heap snapshot exercise.