微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

常驻内存大于 --max-old-space-size 阈值?

如何解决常驻内存大于 --max-old-space-size 阈值?

我正在我的服务器上运行一个 dockerised node.js 应用程序,使用 --max-old-space-size 选项来限制应用程序堆大小。以下输出由 htop 给出:

  PID USER      PRI  NI  VIRT   RES   SHR S cpu% MEM%   TIME+  Command
10158 root       20   0  4284   720   644 S  0.0  0.0  0:00.00 sh -c node --max-old-space-size=512 ./dist/www.js
10159 root       20   0 1841M  929M 29592 S  0.0  3.0  1h31:16 node --max-old-space-size=512 ./dist/www.js
10160 root       20   0 1841M  929M 29592 S  0.0  3.0  0:00.00 node --max-old-space-size=512 ./dist/www.js
10161 root       20   0 1841M  929M 29592 S  0.0  3.0  7:27.13 node --max-old-space-size=512 ./dist/www.js
10162 root       20   0 1841M  929M 29592 S  0.0  3.0  7:26.96 node --max-old-space-size=512 ./dist/www.js
10163 root       20   0 1841M  929M 29592 S  0.0  3.0  7:26.99 node --max-old-space-size=512 ./dist/www.js
10164 root       20   0 1841M  929M 29592 S  0.0  3.0  7:26.64 node --max-old-space-size=512 ./dist/www.js

您可以看到我的应用程序驻留内存 (929M) 远高于我的 max-old-space-size 值 (512MB),那么为什么会出现这种情况?应用程序此时不应该中止吗?

系统信息

Docker version: 19.03.5
node image version: 11.13.0

uname -v
#31~18.04.1-Ubuntu SMP Tue Nov 17 10:48:34 UTC 2020

解决方法

V8 开发者在这里。 --max-old-space-size 标志不直接控制整个进程的内存消耗;它限制了 V8 的托管(即垃圾收集)堆的一部分(最大的部分),这是放置所有 JavaScript 对象的内存块。除了这个“旧空间”,V8还有一个(小得多的)“新空间”;除了V8的托管堆,进程中还有一堆其他的内存消费者:例如,在V8内部,解析器和编译器使用托管堆外的内存;然后除了 V8,Node 也在内存中放置了一堆东西。根据嵌入器(即 Node)和执行代码的具体功能,大字符串和 ArrayBuffers 也可以存在于托管堆之外。

简而言之,--max-old-space-size 为您提供了一个旋钮来影响将使用多少内存,但您在那里设置的限制并没有限制进程的整体内存消耗。 (相比之下,在 Chrome 中,渲染器进程内存的大约三分之一通常是 V8 的托管堆,尽管取决于网站的行为,两个方向都有明显的异常值。我不知道 Node 的典型数字。)

,

您可以在 top 上看到的 RES 是 linux 进程的虚拟内存的非交换区域(有关详细信息,请参阅 man top)。这是解释的简短片段

22. RES  --  Resident Memory Size (KiB)
     A subset of the virtual address space (VIRT) representing the non-swapped 
physical memory a task is currently using.  It is also the sum of the 
RSan,RSfd and RSsh fields.

     It  can  include private anonymous pages,private pages mapped to files 
(including program images and shared libraries) plus shared anonymous pages.  
All such memory is backed by the swap file represented separately under SWAP.

top 的输出不应与 node.js 内存模型元素混淆,因为此输出提供进程的内核视图,而内部内存组织(例如 {{ 1}}) 不可见。

如果你想获得更多关于进程结构(内存映射)的细节,你可以使用node.js,你会看到当前在 RES 中的内存段,但是这些段也不是与 pmap -x <PID> 段混淆(尽管其中一些可以直接映射到 node.js 段)。

node.js 内存映射,是整个进程内存分配的子集,叫做node.js,不要与Resident Set - Resident Memory Size混淆。实际上,RESResident Set 更接近于您在 node.js 中看到的值。 VIRTVIRT 之间的差异将归因于 Resident Set 加载的 unix 共享库(不要与 node.js 模块混淆),进程已打开等)。

node.js 进一步分为 Resident SetCode SegmentStack Segment。顾名思义,Heap Segment 包含可执行代码。 Code Segment 包含进程中当前正在运行的线程的堆栈信息。此 blog 提供了对内存组织的良好概述。

所有这些剩下的就是 Stack Segment。这就是配置参数 Heap Segment 发挥作用的地方。 max-old-space-size 的堆遵循与 Java 垃圾收集内存模型非常相似的内存模型。该参数告诉进程允许有多少对象在连续的垃圾回收周期中存活。然而,这并不意味着这些对象是不可交换的,换句话说,并不是所有设置的 512MB 都将驻留在 node.js 中。这是 RES 的一个子段。 HeapOld Space 的子段。

因此,您在 Heap 中读到的内容与 RESOld Space Sub-Segment 没有直接链接。在短离开应用程序上下文(变量是应用程序未保留的上下文)留下大量垃圾的应用程序中,Heap Segment 将非常小,但是 Old Generation 将迅速增长并且该设置不会帮助您管理进程的内存。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。