如何避免在清除 gc 期间快速增加内存?

How to avoid fast memory increase during scavenge gc?

我有一个基于 restify 的应用程序。我没有内存泄漏,但是在清除 gc 期间我有很大的内存增长,然后是重量级标记扫描 gc 并清理内存。

它会影响我的应用程序的性能。

[2268]   266859 ms: Scavenge 61.5 (119.5) -> 46.0 (119.5) MB, 2.2 ms [allocation failure].
[2268]   267084 ms: Scavenge 63.7 (119.5) -> 48.3 (119.5) MB, 6.2 ms [allocation failure].
[2268]   267289 ms: Scavenge 66.0 (119.5) -> 50.6 (119.5) MB, 2.6 ms [allocation failure].
[2268]   267504 ms: Scavenge 68.3 (119.5) -> 52.8 (119.5) MB, 2.4 ms [allocation failure].
[2268]   267700 ms: Scavenge 70.5 (119.5) -> 55.1 (119.5) MB, 2.7 ms [allocation failure].
....

[2268]   275913 ms: Scavenge 154.2 (183.5) -> 138.8 (183.5) MB, 2.4 ms [allocation failure].
[2268]   276161 ms: Scavenge 157.5 (185.5) -> 142.1 (185.5) MB, 2.7 ms (+ 2.4 ms in 18 steps since last GC) [allocation failure].
[2268]   276427 ms: Scavenge 159.8 (187.5) -> 144.3 (187.5) MB, 2.5 ms (+ 36.3 ms in 236 steps since last GC) [allocation failure].
[2268]   276494 ms: Mark-sweep 147.7 (188.5) -> 43.7 (121.5) MB, 20.1 ms (+ 45.1 ms in 298 steps since start of marking, biggest step 0.5 ms) [GC interrupt] [GC in old space requested].

当我尝试访问不存在的 url

时会发生这种行为
ab -c 100 -n 10000000 -k http://localhost:1337/invalid/url

我真的无法使用节点检查器来跟踪导致如此剧烈的内存增长的原因,因为它会在获取堆快照之前请求完整的 gc。

我有哪些选择来跟踪导致如此快速的内存增长的原因?

如何找出哪些对象在清除后存活但在标记清除 gc 中不存活?

谢谢,

UPDATE 1 所以没有办法看到中年扫荡的内容。这是一个提示,如果您在清理期间看到内存快速增加,但随后它突然随着标记和清除而下降,那么这意味着您的代码创建了大 space 的数据。例如长堆栈跟踪。 Restify 生成巨大的堆栈跟踪,应该在生产中禁用。

您可以尝试 运行 您的 Node 脚本并使用选项 -–expose-gc:

node --expose-gc script.js

这允许从 JS 中手动触发垃圾收集:

global.gc();

手动强制执行垃圾回收时,您可以应用多重快照技术:

  • 在 GC 之前拍摄一个 Snap,在 GC 之后拍摄一个 Snap
  • 然后应用优化
  • 然后在 GC 之前一个 Snap,在 GC 之后一个 Snap

快照允许跟踪导致内存增长的原因。 目标是第二个有更好的成绩"Snap after GC", 与第一个 "Snap after GC" 相比。

据我所知,没有简单的方法可以查看中年内容。但是,如果您在清理期间看到内存快速增加,然后在标记和清除发生时突然下降,那么这通常意味着您的代码在大堆 space 中创建了对象。例如长字符串。

Rectify 为每个 Error 对象生成巨大的堆栈跟踪,这些跟踪大到无法放入新的 space,因此被 mark&sweep 忽略。