Skip to content

Commit 40b6e30

Browse files
authored
zh-CN: 'Diagnostics' translations (#4735)
Refs: #4728
1 parent 2718cac commit 40b6e30

File tree

6 files changed

+536
-0
lines changed

6 files changed

+536
-0
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
title: Diagnostics Guide
3+
layout: docs.hbs
4+
---
5+
6+
# 诊断指南
7+
8+
这些指南在[诊断工作小组](https://github.com/nodejs/diagnostics)中得以创建,
9+
其目的旨在诊断一个程序中的相关问题时能给予相应的指导。
10+
本文档基于用户亲历组织而成。这些历程是一系列互相关联、一步步的措施。
11+
用户应当遵从他们,以便于根据用户上报的事件锁定问题。
12+
13+
以下即是相关的系列诊断指南:
14+
15+
* [内存诊断相关](/zh-cn/docs/guides/diagnostics/memory)
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
---
2+
title: Memory Diagnostics
3+
layout: docs.hbs
4+
---
5+
6+
# 内存诊断相关
7+
8+
在本文档中,你讲学到如何调试与内存相关的一系列问题。
9+
10+
* [内存诊断相关](#memory)
11+
* [内存溢出](#my-process-runs-out-of-memory)
12+
* [相关症状](#symptoms)
13+
* [副作用](#side-effects)
14+
* [Debugging](#debugging)
15+
* [My process utilizes memory inefficiently](#my-process-utilizes-memory-inefficiently)
16+
* [Symptoms](#symptoms-1)
17+
* [Side Effects](#side-effects-1)
18+
* [Debugging](#debugging-1)
19+
20+
## <!--my-process-runs-out-of-memory-->内存耗尽
21+
22+
Node.js _(基于 JavaScript)_ 是一个带垃圾回收功能的语言,故内存泄露是可能是
23+
大量占用引起。通常 Node.js 的应用程序都是多终端、关键业务,以及长时间运行。因
24+
此如能提供一个行之有效的找出内存泄露原因的方法是必须的。
25+
26+
### <!--symptoms-->相关症状
27+
28+
用户观察到内存占用持续增长 _(或快活慢,持续数天乃至数周不等)_,然后发现进程崩溃并
29+
通过进程管理器重启进程。进程或许比之前运行得慢,重启也使得一些特定的请求失败
30+
_(负载均衡返回 502)_
31+
32+
### <!--side-effects-->副作用
33+
34+
* 因为内存耗尽,故重启进程;相关请求也被丢弃。
35+
* 增长的 GC 活动导致 CPU 使用率越来越高,响应速度越来越慢。
36+
* GC 把事件循环机制阻塞住导致了速度变慢。
37+
* 增长的内存交换(由 GC 活动引起)使得进程变慢。
38+
* 没有足够的内存空间来存储一个堆快照。
39+
40+
### <!--debugging-->调试
41+
42+
调试一个内存泄露的问题,我们需要看特定的对象占用了多少内存空间,以及什么变量占有了
43+
他们从而使得垃圾回收。为了使我们有效地调试,我们同时也需要了解变量随时间的分配模式。
44+
45+
* [使用堆剖析器](/en/docs/guides/diagnostics/memory/using-heap-profiler/)
46+
* [使用堆快照](/en/docs/guides/diagnostics/memory/using-heap-snapshot/)
47+
* [GC 跟踪](/en/docs/guides/diagnostics/memory/using-gc-traces)
48+
49+
## <!--my-process-utilizes-memory-inefficiently-->低效率内存使用
50+
51+
### <!--symptoms-1-->相关症状
52+
53+
应用程序占用的内存与我们的预期不符,我们观察到垃圾回收器的活动有所提升。
54+
55+
### <!--side-effects-1-->副作用
56+
57+
* 分页错误数持续增长。
58+
* 较高的 GC 活动以及 CPU 使用率。
59+
60+
### <!--debugging-1-->调试
61+
62+
调试一个内存泄露的问题,我们需要看特定的对象占用了多少内存空间,以及什么变量占有了
63+
他们从而使得垃圾回收。为了使我们有效地调试,我们同时也需要了解变量随时间的分配模式。
64+
65+
* [使用堆剖析器](/en/docs/guides/diagnostics/memory/using-heap-profiler/)
66+
* [使用堆快照](/en/docs/guides/diagnostics/memory/using-heap-snapshot/)
67+
* [GC 跟踪](/en/docs/guides/diagnostics/memory/using-gc-traces)
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
---
2+
title: Memory Diagnostics - Using GC Trace
3+
layout: docs.hbs
4+
---
5+
6+
# 追踪垃圾回收
7+
8+
垃圾回收是如何工作的,实在有太多的东西需要学习。但有一点必须清楚:那便是当 GC 运行
9+
的时候,你的代码是不工作的。
10+
11+
或许你想知道垃圾回收运行的频率,以及需要运行多久,以及结果是什么。
12+
13+
## 如何运行垃圾回收追踪
14+
15+
你可以借助 `--trace_gc` 在控制台输出中看到垃圾回收追踪的信息情况。
16+
17+
```console
18+
$ node --trace_gc app.js
19+
```
20+
21+
或许你不想追踪运行在服务器上的整个进程生命周期里的那些信息,如果是这样的话,请从进程
22+
内部设定把它关闭,这样就不会再追踪了。
23+
24+
以下是如何追踪并打印持续一分钟的 GC 信息示例:
25+
26+
```js
27+
const v8 = require('v8');
28+
v8.setFlagsFromString('--trace_gc');
29+
setTimeout(() => { v8.setFlagsFromString('--notrace_gc'); }, 60e3);
30+
```
31+
32+
### 使用 `--trace_gc` 检查追踪
33+
34+
你得到的垃圾收集器追踪信息看上去如以下样子:
35+
36+
```
37+
[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
38+
39+
[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
40+
```
41+
42+
这里给出如何解析这些信息(以第二行数据举例):
43+
44+
<table>
45+
<tr>
46+
<th>得到的数据</th>
47+
<th>对应解析含义</th>
48+
</tr>
49+
<tr>
50+
<td>23521</td>
51+
<td>正在运行的进程号</td>
52+
</tr>
53+
<tr>
54+
<td>0x10268db0</td>
55+
<td>独立内存地址 (JS 堆实例)</td>
56+
</tr>
57+
<tr>
58+
<td>120</td>
59+
<td>自开始运行的时间(毫秒)</td>
60+
</tr>
61+
<tr>
62+
<td>Mark-sweep</td>
63+
<td>类型 / GC 阶段</td>
64+
</tr>
65+
<tr>
66+
<td>100.7</td>
67+
<td>GC 运行前占有内存(MB)</td>
68+
</tr>
69+
<tr>
70+
<td>122.7</td>
71+
<td>GC 运行前总占有内存(MB)</td>
72+
</tr>
73+
<tr>
74+
<td>100.6</td>
75+
<td>GC 运行后占有内存(MB)</td>
76+
</tr>
77+
<tr>
78+
<td>122.7</td>
79+
<td>GC 运行后总占有内存(MB)</td>
80+
</tr>
81+
<tr>
82+
<td>0.15 / 0.0 <br/>
83+
(average mu = 0.132, current mu = 0.137)</td>
84+
<td>GC 所耗费时间(毫秒)</td>
85+
</tr>
86+
<tr>
87+
<td>deserialize GC in old space requested</td>
88+
<td>GC 原因</td>
89+
</tr>
90+
</table>
91+
92+
## 使用“performance hooks” 追踪你的垃圾回收信息
93+
94+
在 Node.js,你可以使用[performance hooks][] 来跟踪你的垃圾回收信息。
95+
96+
```js
97+
const { PerformanceObserver } = require('perf_hooks');
98+
99+
// Create a performance observer
100+
const obs = new PerformanceObserver((list) => {
101+
const entry = list.getEntries()[0];
102+
/*
103+
The entry would be an instance of PerformanceEntry containing
104+
metrics of garbage collection.
105+
For example:
106+
PerformanceEntry {
107+
name: 'gc',
108+
entryType: 'gc',
109+
startTime: 2820.567669,
110+
duration: 1.315709,
111+
kind: 1
112+
}
113+
*/
114+
});
115+
116+
// Subscribe notifications of GCs
117+
obs.observe({ entryTypes: ['gc'] });
118+
119+
// Stop subscription
120+
obs.disconnect();
121+
```
122+
123+
### 借助“performance hooks”检查追踪信息
124+
125+
你可以在 [PerformanceObserver][] 的回调函数里得到诸如 [PerformanceEntry][] 的 GC
126+
数据。举例说明:
127+
128+
```ts
129+
PerformanceEntry {
130+
name: 'gc',
131+
entryType: 'gc',
132+
startTime: 2820.567669,
133+
duration: 1.315709,
134+
kind: 1
135+
}
136+
```
137+
138+
<table>
139+
<tr>
140+
<th>属性名称</th>
141+
<th>对应解释</th>
142+
</tr>
143+
<tr>
144+
<td>name</td>
145+
<td>进程名称。</td>
146+
</tr>
147+
<tr>
148+
<td>entryType</td>
149+
<td>类型。</td>
150+
</tr>
151+
<tr>
152+
<td>startTime</td>
153+
<td>进程的启动时间(高精度毫秒表示)。</td>
154+
</tr>
155+
<tr>
156+
<td>duration</td>
157+
<td>持续运行时间(毫秒)。</td>
158+
</tr>
159+
<tr>
160+
<td>kind</td>
161+
<td>垃圾回收的类型。</td>
162+
</tr>
163+
<tr>
164+
<td>flags</td>
165+
<td>垃圾回收的其余信息。</td>
166+
</tr>
167+
</table>
168+
169+
欲知更多详情,请查阅
170+
[performance hooks API文档][performance hooks].
171+
172+
## 使用追踪选项诊断内存问题的示例:
173+
174+
A. 如何获取糟糕的内存分配的上下文信息?
175+
1. 假定我们观察到旧内存持续增长。
176+
2. 但根据 GC 的负重来看,堆的最大值并未达到,但进程慢。
177+
3. 回看跟踪的数据,找出在 GC 前后总的堆占用量。
178+
4. 使用 `--max-old-space-size` 降低内存,使得总的内存堆更接近于极限值。
179+
5. 再次运行程序,达到内存耗尽。
180+
6. 该过程的日志将显示失败的上下文信息。
181+
182+
B. 如何确定在堆增长之时,存在内存泄露现象?
183+
1. 假定我们观察到旧内存持续增长。
184+
2. 但根据 GC 的负重来看,堆的最大值并未达到,但进程慢。
185+
3. 回看跟踪的数据,找出在 GC 前后总的堆占用量。
186+
4. 使用 `--max-old-space-size` 降低内存,使得总的内存堆更接近于极限值。
187+
5. 再次运行程序,观察是否内存耗尽。
188+
6. 如果发生内存耗尽,尝试每次提升 10% 的堆内存,反复数次。
189+
如果之前的现象复现被观察到,这就能证明存在内存泄露。
190+
7. 如果不存在内存耗尽,就把内存堆固定在那个值——紧凑的堆减少了内存占用以及对内存压缩的延迟。
191+
192+
C. 如何断定是否存在太多次的垃圾回收,或者因为太多次垃圾回收造成一定的开销?
193+
1. 回顾跟踪数据,尤其关注持续不断的 GC 发生时间隔的一系列数据。
194+
2. 回顾跟踪数据,尤其关注持续不断的 GC 发生时时间消耗的数据。
195+
3. 如果两次 GC 间隙时间小于 GC 所话费的时间,证明程序正处于严重缺乏内存。
196+
4. 如果两次 GC 间隙时间和所话费的时间都非常高,证明该程序应该用一个更小点的堆。
197+
5. 如果两次 GC 的时间远大于 GC 运行的时间,应用程序则相对比较健康。
198+
199+
[performance hooks]: https://nodejs.org/api/perf_hooks.html
200+
[PerformanceEntry]: https://nodejs.org/api/perf_hooks.html#perf_hooks_class_performanceentry
201+
[PerformanceObserver]: https://nodejs.org/api/perf_hooks.html#perf_hooks_class_performanceobserver
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
---
2+
title: Memory Diagnostics - Using Heap Profiler
3+
layout: docs.hbs
4+
---
5+
6+
# 使用内存堆剖析器
7+
8+
为了调试一个与内存相关的问题,我们需要观察特定对象占有了多少内存空间,以及哪些对象占用
9+
了他们触发了垃圾回收。为了有效的调试,我们也需要知道连续时间下我们的变量是如何在内存里
10+
分配的。
11+
12+
内存堆剖析器处于 V8 的顶部,能持续对内存分配进行快照。在这篇文章里我们将设计到内存的
13+
剖析,将使用:
14+
15+
1. 时间轴的分配
16+
2. 采样内存堆剖析器信息
17+
18+
不同于[使用堆快照][]中所涉及到的内容,使用实时堆剖析意在了解一段特定时间下内存分配
19+
情况。
20+
21+
## 堆剖析器 - 时间轴的分配
22+
23+
堆剖析器与堆采样分析器相似,不同处在于它会跟踪每次的内存分配状况,它的开销也就
24+
高于堆采样分析器,所以不建议在生产环境中使用。
25+
26+
> 你可借助 [@mmarchini/observe][] 通过编码方式实现。
27+
28+
### 怎么用堆剖析器?
29+
30+
启动应用程序:
31+
32+
```console
33+
node --inspect index.js
34+
```
35+
36+
> `--inspect-brk` 对于脚本而言是较好的选项。
37+
38+
在 Chrome 中连接开发工具实例,然后:
39+
40+
* 选择 `memory` 选项卡
41+
* 选择 `Allocation instrumentation timeline`
42+
* 开始剖析
43+
44+
![堆剖析器步骤 1][heap profiler tutorial 1]
45+
46+
然后堆剖析就开始运行了,在此我们强烈建议您运行示例,这样便于确认内存相关的问题。
47+
拿本例子而言,我们将使用 `Apache Benchmark` 工具弄出应用程序中的负载。
48+
49+
> 在这个示例中,我们假定堆剖析基于 Web 应用程序。
50+
51+
```console
52+
$ ab -n 1000 -c 5 http://localhost:3000
53+
```
54+
55+
当负载全部请求完毕之后,请按“停止”按钮。
56+
57+
![堆剖析器步骤 2][heap profiler tutorial 2]
58+
59+
然后针对内存分配情况看一下快照。
60+
61+
![堆剖析器步骤 3][heap profiler tutorial 3]
62+
63+
请查阅下列有助于你了解关于更多内存相关术语的[链接部分](#usefull-links)
64+
65+
## 堆剖析的采样
66+
67+
对堆剖析器的采样是在一定时间内持续跟踪内存份分配状况,以及后备内存。由于采样基于低
68+
开销进行,所以它可以用在生产环境。
69+
70+
> 你可以借助 [`heap-profiler`][] 模块,通过编程方式实现此功能。
71+
72+
### 如何对堆剖析进行采样?
73+
74+
启动应用程序:
75+
76+
```console
77+
$ node --inspect index.js
78+
```
79+
80+
> `--inspect-brk` 对于脚本而言是较好的选项。
81+
82+
在 Chrome 中连接开发工具的实例,然后:
83+
84+
1. 选择 `memory` 选项卡
85+
2. 选择 `Allocation sampling`
86+
3. 开始剖析
87+
88+
![堆剖析器步骤 4][heap profiler tutorial 4]
89+
90+
在负载产生后停止剖析器,它会自动生成一个基于堆栈跟踪的内存分配总结。你可以查找某时间
91+
间隔内函数的内存堆分配状况,可以参照下面的例子:
92+
93+
![堆剖析器步骤 5][heap profiler tutorial 5]
94+
95+
## 有帮助的相关链接:
96+
97+
* https://developer.chrome.com/docs/devtools/memory-problems/memory-101/
98+
* https://github.com/v8/sampling-heap-profiler
99+
* https://developer.chrome.com/docs/devtools/memory-problems/allocation-profiler/
100+
101+
[使用堆快照]: /zh-cn/docs/guides/diagnostics/memory/using-heap-snapshot/
102+
[@mmarchini/observe]: https://www.npmjs.com/package/@mmarchini/observe
103+
[`heap-profiler`]: https://www.npmjs.com/package/heap-profile
104+
[heap profiler tutorial 1]: /static/images/docs/guides/diagnostics/heap-profiler-tutorial-1.png
105+
[heap profiler tutorial 2]: /static/images/docs/guides/diagnostics/heap-profiler-tutorial-2.png
106+
[heap profiler tutorial 3]: /static/images/docs/guides/diagnostics/heap-profiler-tutorial-3.png
107+
[heap profiler tutorial 4]: /static/images/docs/guides/diagnostics/heap-profiler-tutorial-4.png
108+
[heap profiler tutorial 5]: /static/images/docs/guides/diagnostics/heap-profiler-tutorial-5.png

0 commit comments

Comments
 (0)