Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

zh-CN: 'Diagnostics' translations #4735

Merged
merged 2 commits into from
Jul 23, 2022
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
15 changes: 15 additions & 0 deletions locale/zh-cn/docs/guides/diagnostics/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
title: Diagnostics Guide
layout: docs.hbs
---

# 诊断指南

这些指南在[诊断工作小组](https://github.com/nodejs/diagnostics)中得以创建,
其目的旨在诊断一个程序中的相关问题时能给予相应的指导。
本文档基于用户亲历组织而成。这些历程是一系列互相关联、一步步的措施。
用户应当遵从他们,以便于根据用户上报的事件锁定问题。

以下即是相关的系列诊断指南:

* [内存诊断相关](/zh-cn/docs/guides/diagnostics/memory)
67 changes: 67 additions & 0 deletions locale/zh-cn/docs/guides/diagnostics/memory/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
title: Memory Diagnostics
layout: docs.hbs
---

# 内存诊断相关

在本文档中,你讲学到如何调试与内存相关的一系列问题。

* [内存诊断相关](#memory)
* [内存溢出](#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-runs-out-of-memory-->内存耗尽

Node.js _(基于 JavaScript)_ 是一个带垃圾回收功能的语言,故内存泄露是可能是
大量占用引起。通常 Node.js 的应用程序都是多终端、关键业务,以及长时间运行。因
此如能提供一个行之有效的找出内存泄露原因的方法是必须的。

### <!--symptoms-->相关症状

用户观察到内存占用持续增长 _(或快活慢,持续数天乃至数周不等)_,然后发现进程崩溃并
通过进程管理器重启进程。进程或许比之前运行得慢,重启也使得一些特定的请求失败
_(负载均衡返回 502)_。

### <!--side-effects-->副作用

* 因为内存耗尽,故重启进程;相关请求也被丢弃。
* 增长的 GC 活动导致 CPU 使用率越来越高,响应速度越来越慢。
* GC 把事件循环机制阻塞住导致了速度变慢。
* 增长的内存交换(由 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-->相关症状

应用程序占用的内存与我们的预期不符,我们观察到垃圾回收器的活动有所提升。

### <!--side-effects-1-->副作用

* 分页错误数持续增长。
* 较高的 GC 活动以及 CPU 使用率。

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

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

* [使用堆剖析器](/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)
201 changes: 201 additions & 0 deletions locale/zh-cn/docs/guides/diagnostics/memory/using-gc-traces.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
---
title: Memory Diagnostics - Using GC Trace
layout: docs.hbs
---

# 追踪垃圾回收

垃圾回收是如何工作的,实在有太多的东西需要学习。但有一点必须清楚:那便是当 GC 运行
的时候,你的代码是不工作的。

或许你想知道垃圾回收运行的频率,以及需要运行多久,以及结果是什么。

## 如何运行垃圾回收追踪

你可以借助 `--trace_gc` 在控制台输出中看到垃圾回收追踪的信息情况。

```console
$ node --trace_gc app.js
```

或许你不想追踪运行在服务器上的整个进程生命周期里的那些信息,如果是这样的话,请从进程
内部设定把它关闭,这样就不会再追踪了。

以下是如何追踪并打印持续一分钟的 GC 信息示例:

```js
const v8 = require('v8');
v8.setFlagsFromString('--trace_gc');
setTimeout(() => { v8.setFlagsFromString('--notrace_gc'); }, 60e3);
```

### 使用 `--trace_gc` 检查追踪

你得到的垃圾收集器追踪信息看上去如以下样子:

```
[19278:0x5408db0] 44 ms: Scavenge 2.3 (3.0) -> 1.9 (4.0) MB, 1.2 / 0.0 ms (average mu = 1.000, current mu = 1.000) allocation failure

[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>
<th>得到的数据</th>
<th>对应解析含义</th>
</tr>
<tr>
<td>23521</td>
<td>正在运行的进程号</td>
</tr>
<tr>
<td>0x10268db0</td>
<td>独立内存地址 (JS 堆实例)</td>
</tr>
<tr>
<td>120</td>
<td>自开始运行的时间(毫秒)</td>
</tr>
<tr>
<td>Mark-sweep</td>
<td>类型 / GC 阶段</td>
</tr>
<tr>
<td>100.7</td>
<td>GC 运行前占有内存(MB)</td>
</tr>
<tr>
<td>122.7</td>
<td>GC 运行前总占有内存(MB)</td>
</tr>
<tr>
<td>100.6</td>
<td>GC 运行后占有内存(MB)</td>
</tr>
<tr>
<td>122.7</td>
<td>GC 运行后总占有内存(MB)</td>
</tr>
<tr>
<td>0.15 / 0.0 <br/>
(average mu = 0.132, current mu = 0.137)</td>
<td>GC 所耗费时间(毫秒)</td>
</tr>
<tr>
<td>deserialize GC in old space requested</td>
<td>GC 原因</td>
</tr>
</table>

## 使用“performance hooks” 追踪你的垃圾回收信息

在 Node.js,你可以使用[performance hooks][] 来跟踪你的垃圾回收信息。

```js
const { PerformanceObserver } = require('perf_hooks');

// Create a performance observer
const obs = new PerformanceObserver((list) => {
const entry = list.getEntries()[0];
/*
The entry would be an instance of PerformanceEntry containing
metrics of garbage collection.
For example:
PerformanceEntry {
name: 'gc',
entryType: 'gc',
startTime: 2820.567669,
duration: 1.315709,
kind: 1
}
*/
});

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

// Stop subscription
obs.disconnect();
```

### 借助“performance hooks”检查追踪信息

你可以在 [PerformanceObserver][] 的回调函数里得到诸如 [PerformanceEntry][] 的 GC
数据。举例说明:

```ts
PerformanceEntry {
name: 'gc',
entryType: 'gc',
startTime: 2820.567669,
duration: 1.315709,
kind: 1
}
```

<table>
<tr>
<th>属性名称</th>
<th>对应解释</th>
</tr>
<tr>
<td>name</td>
<td>进程名称。</td>
</tr>
<tr>
<td>entryType</td>
<td>类型。</td>
</tr>
<tr>
<td>startTime</td>
<td>进程的启动时间(高精度毫秒表示)。</td>
</tr>
<tr>
<td>duration</td>
<td>持续运行时间(毫秒)。</td>
</tr>
<tr>
<td>kind</td>
<td>垃圾回收的类型。</td>
</tr>
<tr>
<td>flags</td>
<td>垃圾回收的其余信息。</td>
</tr>
</table>

欲知更多详情,请查阅
[performance hooks API文档][performance hooks].

## 使用追踪选项诊断内存问题的示例:

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

B. 如何确定在堆增长之时,存在内存泄露现象?
1. 假定我们观察到旧内存持续增长。
2. 但根据 GC 的负重来看,堆的最大值并未达到,但进程慢。
3. 回看跟踪的数据,找出在 GC 前后总的堆占用量。
4. 使用 `--max-old-space-size` 降低内存,使得总的内存堆更接近于极限值。
5. 再次运行程序,观察是否内存耗尽。
6. 如果发生内存耗尽,尝试每次提升 10% 的堆内存,反复数次。
如果之前的现象复现被观察到,这就能证明存在内存泄露。
7. 如果不存在内存耗尽,就把内存堆固定在那个值——紧凑的堆减少了内存占用以及对内存压缩的延迟。

C. 如何断定是否存在太多次的垃圾回收,或者因为太多次垃圾回收造成一定的开销?
1. 回顾跟踪数据,尤其关注持续不断的 GC 发生时间隔的一系列数据。
2. 回顾跟踪数据,尤其关注持续不断的 GC 发生时时间消耗的数据。
3. 如果两次 GC 间隙时间小于 GC 所话费的时间,证明程序正处于严重缺乏内存。
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
108 changes: 108 additions & 0 deletions locale/zh-cn/docs/guides/diagnostics/memory/using-heap-profiler.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
---
title: Memory Diagnostics - Using Heap Profiler
layout: docs.hbs
---

# 使用内存堆剖析器

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

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

1. 时间轴的分配
2. 采样内存堆剖析器信息

不同于[使用堆快照][]中所涉及到的内容,使用实时堆剖析意在了解一段特定时间下内存分配
情况。

## 堆剖析器 - 时间轴的分配

堆剖析器与堆采样分析器相似,不同处在于它会跟踪每次的内存分配状况,它的开销也就
高于堆采样分析器,所以不建议在生产环境中使用。

> 你可借助 [@mmarchini/observe][] 通过编码方式实现。

### 怎么用堆剖析器?

启动应用程序:

```console
node --inspect index.js
```

> `--inspect-brk` 对于脚本而言是较好的选项。

在 Chrome 中连接开发工具实例,然后:

* 选择 `memory` 选项卡
* 选择 `Allocation instrumentation timeline`
* 开始剖析

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

然后堆剖析就开始运行了,在此我们强烈建议您运行示例,这样便于确认内存相关的问题。
拿本例子而言,我们将使用 `Apache Benchmark` 工具弄出应用程序中的负载。

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

```console
$ ab -n 1000 -c 5 http://localhost:3000
```

当负载全部请求完毕之后,请按“停止”按钮。

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

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

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

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

## 堆剖析的采样

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

> 你可以借助 [`heap-profiler`][] 模块,通过编程方式实现此功能。

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

启动应用程序:

```console
SEWeiTung marked this conversation as resolved.
Show resolved Hide resolved
$ node --inspect index.js
```

> `--inspect-brk` 对于脚本而言是较好的选项。

在 Chrome 中连接开发工具的实例,然后:

1. 选择 `memory` 选项卡
2. 选择 `Allocation sampling`
3. 开始剖析

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

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

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

## 有帮助的相关链接:

* https://developer.chrome.com/docs/devtools/memory-problems/memory-101/
* https://github.com/v8/sampling-heap-profiler
* https://developer.chrome.com/docs/devtools/memory-problems/allocation-profiler/

[使用堆快照]: /zh-cn/docs/guides/diagnostics/memory/using-heap-snapshot/
[@mmarchini/observe]: https://www.npmjs.com/package/@mmarchini/observe
[`heap-profiler`]: https://www.npmjs.com/package/heap-profile
[heap profiler tutorial 1]: /static/images/docs/guides/diagnostics/heap-profiler-tutorial-1.png
[heap profiler tutorial 2]: /static/images/docs/guides/diagnostics/heap-profiler-tutorial-2.png
[heap profiler tutorial 3]: /static/images/docs/guides/diagnostics/heap-profiler-tutorial-3.png
[heap profiler tutorial 4]: /static/images/docs/guides/diagnostics/heap-profiler-tutorial-4.png
[heap profiler tutorial 5]: /static/images/docs/guides/diagnostics/heap-profiler-tutorial-5.png
Loading