Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 8 additions & 18 deletions locale/zh-cn/docs/guides/diagnostics/memory/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ layout: docs.hbs
* [内存溢出](#my-process-runs-out-of-memory)
* [相关症状](#symptoms)
* [副作用](#side-effects)
* [Debugging](#debugging)
* [My process utilizes memory inefficiently](#my-process-utilizes-memory-inefficiently)
* [Symptoms](#symptoms-1)
* [Side Effects](#side-effects-1)
* [Debugging](#debugging-1)
* [低效率内存使用](#my-process-utilizes-memory-inefficiently)
* [相关症状](#symptoms-1)
* [副作用](#side-effects-1)
* [调试](#debugging)

## <!--my-process-runs-out-of-memory-->内存耗尽

Expand All @@ -37,15 +36,6 @@ _(负载均衡返回 502)_。
* 增长的内存交换(由 GC 活动引起)使得进程变慢。
* 没有足够的内存空间来存储一个堆快照。

### <!--debugging-->调试

调试一个内存泄露的问题,我们需要看特定的对象占用了多少内存空间,以及什么变量占有了
他们从而使得垃圾回收。为了使我们有效地调试,我们同时也需要了解变量随时间的分配模式。

* [使用堆剖析器](/en/docs/guides/diagnostics/memory/using-heap-profiler/)
* [使用堆快照](/en/docs/guides/diagnostics/memory/using-heap-snapshot/)
* [GC 跟踪](/en/docs/guides/diagnostics/memory/using-gc-traces)

## <!--my-process-utilizes-memory-inefficiently-->低效率内存使用

### <!--symptoms-1-->相关症状
Expand All @@ -57,11 +47,11 @@ _(负载均衡返回 502)_。
* 分页错误数持续增长。
* 较高的 GC 活动以及 CPU 使用率。

### <!--debugging-1-->调试
## <!--debugging-->调试

调试一个内存泄露的问题,我们需要看特定的对象占用了多少内存空间,以及什么变量占有了
他们从而使得垃圾回收。为了使我们有效地调试,我们同时也需要了解变量随时间的分配模式。

* [使用堆剖析器](/en/docs/guides/diagnostics/memory/using-heap-profiler/)
* [使用堆快照](/en/docs/guides/diagnostics/memory/using-heap-snapshot/)
* [GC 跟踪](/en/docs/guides/diagnostics/memory/using-gc-traces)
* [使用堆剖析器](/zh-cn/docs/guides/diagnostics/memory/using-heap-profiler/)
* [使用堆快照](/zh-cn/docs/guides/diagnostics/memory/using-heap-snapshot/)
* [GC 跟踪](/zh-cn/docs/guides/diagnostics/memory/using-gc-traces)
38 changes: 23 additions & 15 deletions locale/zh-cn/docs/guides/diagnostics/memory/using-gc-traces.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ setTimeout(() => { v8.setFlagsFromString('--notrace_gc'); }, 60e3);
[23521:0x10268b000] 120 ms: Mark-sweep 100.7 (122.7) -> 100.6 (122.7) MB, 0.15 / 0.0 ms (average mu = 0.132, current mu = 0.137) deserialize GC in old space requested
```

这里给出如何解析这些信息(以第二行数据举例)
以上面第二行数据举例,这里给出如何解析这些信息:

<table>
<tr>
Expand All @@ -48,7 +48,7 @@ setTimeout(() => { v8.setFlagsFromString('--notrace_gc'); }, 60e3);
</tr>
<tr>
<td>23521</td>
<td>正在运行的进程号</td>
<td>正在运行的线程号</td>
</tr>
<tr>
<td>0x10268db0</td>
Expand All @@ -64,25 +64,32 @@ setTimeout(() => { v8.setFlagsFromString('--notrace_gc'); }, 60e3);
</tr>
<tr>
<td>100.7</td>
<td>GC 运行前占有内存(MB)</td>
<td>GC 运行前占有内存(MiB)</td>
</tr>
<tr>
<td>122.7</td>
<td>GC 运行前总占有内存(MB)</td>
<td>GC 运行前总占有内存(MiB)</td>
</tr>
<tr>
<td>100.6</td>
<td>GC 运行后占有内存(MB)</td>
<td>GC 运行后占有内存(MiB)</td>
</tr>
<tr>
<td>122.7</td>
<td>GC 运行后总占有内存(MB)</td>
<td>GC 运行后总占有内存(MiB)</td>
</tr>
<tr>
<td>0.15 / 0.0 <br/>
(average mu = 0.132, current mu = 0.137)</td>
<td>0.15</td>
<td>GC 所耗费时间(毫秒)</td>
</tr>
<tr>
<td>0.0</td>
<td>GC 垃圾回收回调所话费的时间(毫秒)</td>
</tr>
<tr>
<td>(average mu = 0.132, current mu = 0.137)</td>
<td>增变因子利用率(0 —— 1 之间)</td>
</tr>
<tr>
<td>deserialize GC in old space requested</td>
<td>GC 原因</td>
Expand All @@ -100,8 +107,8 @@ const { PerformanceObserver } = require('perf_hooks');
const obs = new PerformanceObserver((list) => {
const entry = list.getEntries()[0];
/*
The entry would be an instance of PerformanceEntry containing
metrics of garbage collection.
The entry is an instance of PerformanceEntry containing
metrics of a single garbage collection event.
For example:
PerformanceEntry {
name: 'gc',
Expand All @@ -113,7 +120,7 @@ const obs = new PerformanceObserver((list) => {
*/
});

// Subscribe notifications of GCs
// Subscribe to notifications of GCs
obs.observe({ entryTypes: ['gc'] });

// Stop subscription
Expand Down Expand Up @@ -175,15 +182,15 @@ A. 如何获取糟糕的内存分配的上下文信息?
1. 假定我们观察到旧内存持续增长。
2. 但根据 GC 的负重来看,堆的最大值并未达到,但进程慢。
3. 回看跟踪的数据,找出在 GC 前后总的堆占用量。
4. 使用 `--max-old-space-size` 降低内存,使得总的内存堆更接近于极限值。
5. 再次运行程序,达到内存耗尽
4. 使用 [`--max-old-space-size`][] 降低内存,使得总的内存堆更接近于极限值。
5. 再次不断地运行程序,直到内存耗尽
6. 该过程的日志将显示失败的上下文信息。

B. 如何确定在堆增长之时,存在内存泄露现象?
1. 假定我们观察到旧内存持续增长。
2. 但根据 GC 的负重来看,堆的最大值并未达到,但进程慢。
3. 回看跟踪的数据,找出在 GC 前后总的堆占用量。
4. 使用 `--max-old-space-size` 降低内存,使得总的内存堆更接近于极限值。
4. 使用 [`--max-old-space-size`][] 降低内存,使得总的内存堆更接近于极限值。
5. 再次运行程序,观察是否内存耗尽。
6. 如果发生内存耗尽,尝试每次提升 10% 的堆内存,反复数次。
如果之前的现象复现被观察到,这就能证明存在内存泄露。
Expand All @@ -196,6 +203,7 @@ C. 如何断定是否存在太多次的垃圾回收,或者因为太多次垃
4. 如果两次 GC 间隙时间和所话费的时间都非常高,证明该程序应该用一个更小点的堆。
5. 如果两次 GC 的时间远大于 GC 运行的时间,应用程序则相对比较健康。

[performance hooks]: https://nodejs.org/api/perf_hooks.html
[PerformanceEntry]: https://nodejs.org/api/perf_hooks.html#perf_hooks_class_performanceentry
[PerformanceObserver]: https://nodejs.org/api/perf_hooks.html#perf_hooks_class_performanceobserver
[`--max-old-space-size`]: https://nodejs.org/api/cli.html#--max-old-space-sizesize-in-megabytes
[performance hooks]: https://nodejs.org/api/perf_hooks.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ layout: docs.hbs

# 使用内存堆剖析器

为了调试一个与内存相关的问题,我们需要观察特定对象占有了多少内存空间,以及哪些对象占用
了他们触发了垃圾回收。为了有效的调试,我们也需要知道连续时间下我们的变量是如何在内存里
分配的。

内存堆剖析器处于 V8 的顶部,能持续对内存分配进行快照。在这篇文章里我们将设计到内存的
剖析,将使用:

Expand All @@ -23,7 +19,7 @@ layout: docs.hbs
堆剖析器与堆采样分析器相似,不同处在于它会跟踪每次的内存分配状况,它的开销也就
高于堆采样分析器,所以不建议在生产环境中使用。

> 你可借助 [@mmarchini/observe][] 通过编码方式实现
> 你可借助 [@mmarchini/observe][] 通过编码方式来启动和停止剖析器

### 怎么用堆剖析器?

Expand All @@ -43,8 +39,9 @@ node --inspect index.js

![堆剖析器步骤 1][heap profiler tutorial 1]

然后堆剖析就开始运行了,在此我们强烈建议您运行示例,这样便于确认内存相关的问题。
拿本例子而言,我们将使用 `Apache Benchmark` 工具弄出应用程序中的负载。
堆剖析一旦开始运行,我们强烈建议您运行示例,这样便于确认内存相关的问题。
举个例子:如果我们对一个 Web 应用程序进行堆剖析,`Apache Benchmark`
可以用来产出(模拟)应用程序中的负载。

> 在这个示例中,我们假定堆剖析基于 Web 应用程序。

Expand All @@ -56,18 +53,18 @@ $ ab -n 1000 -c 5 http://localhost:3000

![堆剖析器步骤 2][heap profiler tutorial 2]

然后针对内存分配情况看一下快照
最后针对内存分配情况看一下快照

![堆剖析器步骤 3][heap profiler tutorial 3]

请查阅下列有助于你了解关于更多内存相关术语的[链接部分](#usefull-links) 。
请查阅下列有助于你了解关于更多内存相关术语的[链接部分](#useful-links) 。

## 堆剖析的采样

对堆剖析器的采样是在一定时间内持续跟踪内存份分配状况,以及后备内存。由于采样基于低
对堆剖析器的采样是在一定时间内持续跟踪内存份分配状况和后备内存。由于采样基于低
开销进行,所以它可以用在生产环境。

> 你可以借助 [`heap-profiler`][] 模块,通过编程方式实现此功能
> 你可以借助 [`heap-profiler`][] 模块,通过编程方式控制堆内存剖析的开与关

### 如何对堆剖析进行采样?

Expand All @@ -87,8 +84,8 @@ $ node --inspect index.js

![堆剖析器步骤 4][heap profiler tutorial 4]

在负载产生后停止剖析器,它会自动生成一个基于堆栈跟踪的内存分配总结。你可以查找某时间
间隔内函数的内存堆分配状况,可以参照下面的例子:
在负载产生后停止剖析器,它会自动生成一个基于堆栈跟踪的内存分配总结。你可以关注
函数的内存堆分配状况,可以参照下面的例子:

![堆剖析器步骤 5][heap profiler tutorial 5]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,10 @@ Heap.20190718.133405.15554.0.001.heapsnapshot
require('v8').writeHeapSnapshot();
```

请查阅 [writeHeapSnapshot 文档][] 了解文件名的可用选项。
请查阅 [`writeHeapSnapshot` 文档][] 了解文件名的可用选项。

在不停掉进程的前提下你需要有一个方式来生成快照,建议在 http handler 里调用,亦或是从操作
系统中对某个信号量做出反应。但你需小心一点:不要暴漏触发生成快照的 http 终端地址,它不应该
在不停掉进程的前提下你需要有一个方式来生成快照,建议在 HTTP Handler 里调用,亦或是从操作
系统中对某个信号量做出反应。但你需小心一点:不要暴漏触发生成快照的 Http 终端地址,它不应该
被其他人直接访问。

对于 v11.13.0 之前的 Node.js 版本,请借助 [heapdump 包][]来实现。
Expand Down Expand Up @@ -111,7 +111,7 @@ done < <(cat out | tail -n +2 | head -n -1)
exec 3>&-
```

以下是与检查器协议一起使用的内存分析工具的详尽列表
这里提供一份与检查器协议一起使用的内存分析工具的详尽列表

* [适用于 Node.js 的 OpenProfiling][openprofiling]

Expand All @@ -136,7 +136,7 @@ exec 3>&-
[take a heap snapshot image]: /static/images/docs/guides/diagnostics/snapshot.png
[内存快照信号量标志符]: https://nodejs.org/api/cli.html#--heapsnapshot-signalsignal
[heapdump 包]: https://www.npmjs.com/package/heapdump
[writeHeapSnapshot 文档]: https://nodejs.org/api/v8.html#v8_v8_writeheapsnapshot_filename
[`writeHeapSnapshot` 文档]: https://nodejs.org/api/v8.html#v8_v8_writeheapsnapshot_filename
[openprofiling]: https://github.com/vmarchaud/openprofiling-node
[load button image]: /static/images/docs/guides/diagnostics/load-snapshot.png
[comparison image]: /static/images/docs/guides/diagnostics/compare.png
Expand Down