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

Posted

技术标签:

【中文标题】如何避免在清除 gc 期间快速增加内存?【英文标题】:How to avoid fast memory increase during scavenge gc? 【发布时间】:2015-11-23 03:20:03 【问题描述】:

我有一个基于 restify 的应用程序。我没有内存泄漏,但是在清除 gc 期间我的内存增长很大,然后是重量级的 mark-sweep 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 中没有幸存下来?

谢谢,

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

【问题讨论】:

【参考方案1】:

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

Rectify 会为每个 Error 对象生成巨大的堆栈跟踪,这些堆栈跟踪大到无法放入新空间,因此会被标记和扫描忽略。

【讨论】:

【参考方案2】:

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

node --expose-gc script.js

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

global.gc();

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

在 GC 之前取一个 Snap,在 GC 之后取一个 Snap 然后应用优化 然后一个 Snap 之前,一个 Snap 之后 GC

快照允许跟踪导致内存增长的原因。 目标是第二次“Snap after GC”有更好的结果, 与第一个“GC后快照”相比。

【讨论】:

抱歉这个愚蠢的问题,但是如何在不调用 gc 的情况下拍摄快照? 在执行你的节点应用程序后,启动节点检查器并连接到http://127.0.0.1:8080/debug?port=5858(或你的主机而不是本地主机),例如 Chrome 开发工具,然后转到“配置文件”选项卡。你会在那里找到“Take Heap Snapshot”和“Record Heap Allocation”。 是的,这就是我要解决的问题。我在日志中看到在制作完整快照之前它运行的是完整的 gc 垃圾收集仅在获取堆快照时自动强制执行。换句话说:“记录堆分配”。它将在整个录制过程中拍摄快照,然后在最后进行最后一张快照。 developers.google.com/web/tools/profile-performance/… 奇怪,我仍然看到在堆分配快照期间只调用了 Mark-sweep gc

以上是关于如何避免在清除 gc 期间快速增加内存?的主要内容,如果未能解决你的问题,请参考以下文章

清除 R 会话分配的内存(gc()没有帮助!)[重复]

避免、发现和消除 Cocoa 中的内存泄漏

百度地图覆盖物增加到地图后进行清除,是不是会被内存回收

如何避免内存泄漏

JavaScript性能优化1——内存管理(JS垃圾回收机制引用计数标记清除标记整理)

Qt内存使用情况检查