【问题标题】:Resident memory greater than --max-old-space-size threshold?常驻内存大于 --max-old-space-size 阈值?
【发布时间】:2021-03-02 05:49:15
【问题描述】:

我正在我的服务器上运行一个 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

【问题讨论】:

    标签: node.js docker ubuntu memory-management v8


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

      您可以在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 内存模型元素混淆,因为此输出提供进程的内核视图,而内部内存组织(如node.js)则不是可见。

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

      node.js 内存映射是分配的整个进程内存的子集,称为Resident Set,不应与Resident Memory Size - RES 混淆。事实上,node.jsResident Set 更接近于您在VIRT 中可以看到的值。 VIRTResident Set 之间的差异将归因于由 node.js 加载的 unix 共享库(不要与 node.js 模块混淆)、进程已打开的文件等)。

      Resident Set 进一步分为Code SegmentStack SegmentHeap Segment。顾名思义,Code Segment 包含可执行代码。 Stack Segment 包含进程中当前正在运行的线程的堆栈信息。这个blog 很好地概述了内存组织。

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

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

      【讨论】:

      • 其中一些细节不正确。 “驻留集”和“驻留内存”是同义词; “VIRT”不属于库,它是保留的地址空间,与实际内存使用完全无关(除了它是一个上限)。 JavaScript 堆始终是 RES 的一部分,并且不存在短期对象使整个堆大量增长的情况(因为新空间的大小有限且经常被收集;如果有任何增长,那么它就是旧空间,由相关标志控制)。
      • 您混淆了top 提供的unix 角度的RESnode.jsResident Setnode.js 是 linux 中的一个进程,RES 是物理内存中有多少进程内存的度量。
      • man top 将“RES”称为“驻留内存大小”,man htop(OP 使用)将其称为“驻留集大小”。这是同一件事。您可能希望使用不同的术语来描述进程内存中的不同事物。 - 您链接的 deepu.tech 博客文章包含许多不准确之处(其中一些已过时 [例如,不再有单元空间],缺少一些东西 [例如,Unix“堆”比 V8 托管堆更多],有些语句完全错误[例如,字符串永远不会在堆栈中])。
      • top 没有注意到进程是node.js。 Astop 是操作系统的实用程序,您需要从操作系统的角度读取它的输出。如果你想从node.js的角度去读,你会得到错误的结论,就像linux的RES和node.js的Resident Set一样。
      • 当然top 对待所有进程都是一样的。 “驻留集”没有特定于节点的定义,所有进程都相同:使用的物理内存。当然,如果我们想进一步了解 Node 究竟将内存用于什么用途,那么top 也帮不了我们。
      猜你喜欢
      • 2018-11-10
      • 1970-01-01
      • 2019-11-20
      • 1970-01-01
      • 2021-01-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-27
      相关资源
      最近更新 更多