【问题标题】:Node JS application rising memory pressureNode JS 应用内存压力上升
【发布时间】:2017-01-06 11:09:59
【问题描述】:

我正在开发一个 Node JS 应用程序并面临严重的内存压力问题。以下是应用程序启动后 60 分钟内的内存使用趋势(在恒定负载下):

内存使用率急剧上升到 95%,但之后保持相当稳定。

我之前和之后都进行了内存转储,但我捕获转储的方式似乎存在一些问题,因为两个转储文件之间的差异只有几 MB。

这是堆转储在 chrome 分析中的样子:

但我对此很陌生,不知道我应该寻找什么。

谁能提供任何关于如何找出这里的内存问题、如何检测内存泄漏或如何理解这里提到的 heapdump 文件的指示?

节点版本:0.12.14

- find node_modules -type f -name "*.node" - 的输出如下:

node_modules/simpleflake/node_modules/bignum/build/Release/bignum.node
node_modules/simpleflake/node_modules/bignum/build/Release/obj.target/bignum.node
node_modules/heapdump/build/Release/addon.node
node_modules/heapdump/build/Release/obj.target/addon.node
node_modules/couchbase/build/Release/couchbase_impl.node

- npm list --depth=0 - 的输出如下:

aws-sdk@2.5.3
body-parser@1.11.0
check-types@6.0.0
couchbase@2.2.2
elasticsearch@10.1.3
expect@1.20.2
express@4.14.0
jshint@2.9.3
minimist@1.1.3
mocha@2.5.3
moment@2.9.0
morgan@1.5.3
newrelic@1.30.0
request@2.53.0
simpleflake@1.0.0
underscore@1.7.0
why-is-node-running@1.2.2 (https://github.com/mindtickle/why-is-node-running.git#96f3c8da54b110e8a9a1423361d2da7c125784f6)
winston@1.0.2
winston-aws-cloudwatch@0.4.2

另外,是否有可能查看我正在使用的任何包是否会导致内存泄漏?

提前致谢。

【问题讨论】:

  • 您使用的是什么版本的节点? node --version 你在运行什么原生模块? find node_modules -type f -name "*.node"
  • 你是如何生成物理内存图的?
  • 嗨,马特,感谢您的关注,我已经编辑了这个问题,提供了有关节点版本和所用软件包的更多信息。并且,该图取自 newrelic(newrelic 代理在服务器上运行,提供实时服务器数据)
  • 好的。我不知道 newrelic 是如何收集到的。。在系统处于完整内存状态时尝试freecat /proc/meminfo
  • @Matt - 毫无疑问,这些内存使用数字是否正确。系统内存不足,应用程序崩溃。此外,newrelic 代理已经在这台机器上运行了很长时间。

标签: node.js memory memory-leaks node-heapdump


【解决方案1】:

要查看的两个主要领域是对象保留和本机泄漏。这适用于在 VM 上运行的大多数垃圾收集语言

很可能,您的应用程序或模块中有一些东西保留了对对象的引用并填满了对象空间。

接下来是使用本机代码并泄漏本机内存的模块,它们不会出现在 GC 对象空间中。

那么 Node.js 本身可能存在原生泄漏,由于用户数量众多,这不太可能,但始终存在这种可能性,尤其是对于旧版本的 Node.js。

应用内存和垃圾回收

在打开节点垃圾收集日志的情况下运行您的应用程序

node --trace_gc --trace_gc_verbose app.js

这将提供有关每个 GC 事件的信息块。主要信息是第一行,它告诉您 Node.js 在 GC 之前和之后使用了多少内存 ->

[97577:0x101804a00]    38841 ms: Scavenge 369.0 (412.2) -> 356.0 (414.2) MB, 5.4 / 0 ms [allocation failure].

--trace_gc_verbose 为您提供此后的所有行以及每个内存空间的更多详细信息。

[97577:0x101804a00] Memory allocator,   used: 424180 KB, available: 1074956 KB
[97577:0x101804a00] New space,          used:    789 KB, available:  15334 KB, committed:  32248 KB
[97577:0x101804a00] Old space,          used: 311482 KB, available:      0 KB, committed: 321328 KB
[97577:0x101804a00] Code space,         used:  22697 KB, available:   3117 KB, committed:  26170 KB
[97577:0x101804a00] Map space,          used:  15031 KB, available:   3273 KB, committed:  19209 KB
[97577:0x101804a00] Large object space, used:  14497 KB, available: 1073915 KB, committed:  14640 KB
[97577:0x101804a00] All spaces,         used: 364498 KB, available: 1095640 KB, committed: 413596 KB
[97577:0x101804a00] External memory reported:  19448 KB
[97577:0x101804a00] Total time spent in GC  : 944.0 ms

绘制这些值通常看起来像一个锯齿。这种“锯齿状”发生在多个级别,因为不同的内存空间被填满,达到限制,然后被垃圾收集回来。这张来自Dynatraces About Performance blogUnderstanding Garbage Collection and hunting Memory Leaks in Node.js 上的帖子的图表显示了一个应用程序正在慢慢增加它的对象空间

随着时间的推移,您应该会看到哪些内存区域会增长,哪些不会增长,这可以为您提供一些上下文,了解哪些代码保留了对象引用。如果您在堆中看不到任何内存增长,则可能存在真正的内存泄漏,无论是在本机模块中还是在 Node.js 本身中。

进程内存

node 应用程序报告的由垃圾收集器使用的内存(称为“堆”)并不总是与操作系统为进程报告的内存相匹配。操作系统可以分配大量的内存,而 Node.js 当前可能没有使用。这可能是正常的,如果操作系统没有受到内存压力并且 Node.js 垃圾收集对象,则会有差异,因为操作系统会为进程保留额外的内存空间。当某些东西发生泄漏并且分配的进程内存不断增长时,这也可能是异常的。

随您的应用收集一些操作系统内存信息

node --trace_gc app.js &
pid=$!

while sleep 15; do
  pss=$(sudo awk '/^Pss/{p=p+$2}END{print p}' /proc/$pid/smaps)
  echo "$(date) pss[$pss]"
done

这将为您提供与 GC 输出一致的日期和内存值(以字节为单位)以进行比较:

[531:0x101804a00]    12539 ms: Scavenge 261.6 (306.4) -> 246.6 (307.4) MB, 5.0 / 0 ms [allocation failure].
Tue Aug 30 12:34:46 UTC 2016 pss[3396192]

smaps 中的Pss 数字考虑了共享内存重复,因此比ps 使用的Rss 数字更准确

堆转储

如果您确实发现垃圾回收日志内存与操作系统进程内存增长相匹配,那么很可能是应用程序或它所依赖的模块保留了对旧对象的引用,您需要开始查看堆转储以确定那些对象是。

使用 Chrome dev tools 并在调试模式下运行您的应用,以便您可以附加到它并创建堆转储。

node --inspect app.js

当检测到 Node 进程时,您将在左上方看到一个小 Node 图标,单击该图标以打开专用于节点应用程序的 Devtools 窗口。在应用程序启动时拍摄堆快照。在内存增长中间的某个地方。然后接近最大时(在崩溃之前!)。

比较后两个转储与第一个转储中寻找大对象或重复对象引用的 3 个快照。


本机泄漏

如果 GC 日志没有显示内存增加,但操作系统对节点进程有增加,则可能是本机泄漏。

OSX 有一个名为leaks 的有用开发工具,它可以在进程中找到未引用的内存,而无需完全调试。我相信valgrind 可以和--leak-check=yes 做同样的事情。

这些可能无法识别问题所在,只是确认问题是本机泄漏。

模块

要在您的应用中查找本机模块,请使用以下命令:

find node_modules -type f -name "*.node"

对于这些模块中的每一个,制作一个关于每个模块在您的应用程序中的使用方式的小型测试用例,并在检查泄漏时对其进行压力测试。

您还可以尝试暂时从应用程序中排除或禁用任何本机模块,以排除它们或将它们突出显示为原因

Node.js

如果可能,请尝试其他版本的 Node.js。移至最新的主要版本、最新的次要/补丁或返回主要版本以查看内存配置文件是否有任何改进或变化。

从 v0.x 开始,v4.x 或 v6.x Node.js 版本在常规内存管理方面有了巨大改进。如果您可以更新,该问题可能会通过错误修复或V8 更新来解决。

【讨论】:

  • 感谢您的引用 - 这是涵盖追踪内存问题的技术和示例代码的完整博客文章:apmblog.dynatrace.com/2015/11/04/…
  • 很棒的帖子。抱歉,我太忙于尝试在编辑器中创建指向帖子网址的图片链接,我也忘记添加文本了。
猜你喜欢
  • 2017-12-15
  • 1970-01-01
  • 2014-09-30
  • 2015-09-22
  • 2015-12-21
  • 2014-12-28
  • 1970-01-01
  • 2013-12-08
  • 2014-09-01
相关资源
最近更新 更多