如何确定给定内存限制的节点 --max_old_space_size?
How to determine Node --max_old_space_size for a given memory limit?
我们 运行 在 Docker 硬内存上限为 1GB、2GB 或 4GB 的容器中处理节点。每个容器通常只是 运行 一个节点进程(可能加上一个很小的 shell 脚本包装器)。出于这个问题的目的,我们假设 Node 进程从不分叉更多进程。
对于我们更大的容器,如果我们自己不设置 --max_old_space_size,那么在我们使用的 Node 版本中(在 64 位机器上)它 defaults to 1400MB. (This will change to 2048MB 在更高版本的节点.)
理想情况下,我们希望我们的 Node 进程尽可能多地使用容器,而不会越过 运行 内存不足。问题是——我们应该使用什么数字?我的理解是,这个特定的标志调整了 Node 使用的最大内存池之一的大小,但它不是唯一的池——例如,堆的 "non-old" 部分,堆栈等。多少我应该在设置此标志时从容器的大小中减去,以便远离 cgroup 内存限制,但仍最大限度地使用此容器中允许的内存量吗?
我确实注意到,从定义 kMaxOldSpaceSizeHugeMemoryDevice 的同一个地方,它看起来默认 "max semi space" 是 16MB,默认 "max executable size" 是 512MB。所以我怀疑这意味着在确定此标志的值时我应该从容器的内存限制中减去至少 528。但是 Node 使用内存肯定还有其他方式吗?
(更具体地说,我们是一家托管服务商,向我们的用户出售特定大小的容器,其中大多数用户将它们用于节点进程。我们希望能够建议我们的客户使用什么标志进行设置,以便它们既不会被我们的限制杀死,也不会向我们支付 Node 的配置不允许它们实际使用的容量。)
不幸的是,这个问题没有特别令人满意的答案。
您找到的常量控制 garbage-collected 堆的大小,但正如您已经猜到的那样,有许多方法可以消耗不属于该堆的内存:
- 例如,大字符串和大类型数组通常由嵌入器(即节点及其模块,而不是 V8 本身)管理,并且在 GC 堆之外。
- 一般来说,节点模块可以消耗他们想要的任何内存。大概您不想限制您的客户可以使用哪些模块 运行,但这意味着您也无法预测这些模块将需要多少内存。
- V8 还使用 GC 堆外的临时内存进行解析和编译。数字取决于 运行 中的代码,从几千字节到千兆字节或更多(例如,对于巨大的 asm.js 代码库)一切皆有可能。这些是相对 short-lived 的内存消耗峰值,因此一方面您可能不想限制 long-lived 堆内存来解决它们,但另一方面这意味着它们可以使您的进程 运行进入系统限制。
我们 运行 在 Docker 硬内存上限为 1GB、2GB 或 4GB 的容器中处理节点。每个容器通常只是 运行 一个节点进程(可能加上一个很小的 shell 脚本包装器)。出于这个问题的目的,我们假设 Node 进程从不分叉更多进程。
对于我们更大的容器,如果我们自己不设置 --max_old_space_size,那么在我们使用的 Node 版本中(在 64 位机器上)它 defaults to 1400MB. (This will change to 2048MB 在更高版本的节点.)
理想情况下,我们希望我们的 Node 进程尽可能多地使用容器,而不会越过 运行 内存不足。问题是——我们应该使用什么数字?我的理解是,这个特定的标志调整了 Node 使用的最大内存池之一的大小,但它不是唯一的池——例如,堆的 "non-old" 部分,堆栈等。多少我应该在设置此标志时从容器的大小中减去,以便远离 cgroup 内存限制,但仍最大限度地使用此容器中允许的内存量吗?
我确实注意到,从定义 kMaxOldSpaceSizeHugeMemoryDevice 的同一个地方,它看起来默认 "max semi space" 是 16MB,默认 "max executable size" 是 512MB。所以我怀疑这意味着在确定此标志的值时我应该从容器的内存限制中减去至少 528。但是 Node 使用内存肯定还有其他方式吗?
(更具体地说,我们是一家托管服务商,向我们的用户出售特定大小的容器,其中大多数用户将它们用于节点进程。我们希望能够建议我们的客户使用什么标志进行设置,以便它们既不会被我们的限制杀死,也不会向我们支付 Node 的配置不允许它们实际使用的容量。)
不幸的是,这个问题没有特别令人满意的答案。
您找到的常量控制 garbage-collected 堆的大小,但正如您已经猜到的那样,有许多方法可以消耗不属于该堆的内存:
- 例如,大字符串和大类型数组通常由嵌入器(即节点及其模块,而不是 V8 本身)管理,并且在 GC 堆之外。
- 一般来说,节点模块可以消耗他们想要的任何内存。大概您不想限制您的客户可以使用哪些模块 运行,但这意味着您也无法预测这些模块将需要多少内存。
- V8 还使用 GC 堆外的临时内存进行解析和编译。数字取决于 运行 中的代码,从几千字节到千兆字节或更多(例如,对于巨大的 asm.js 代码库)一切皆有可能。这些是相对 short-lived 的内存消耗峰值,因此一方面您可能不想限制 long-lived 堆内存来解决它们,但另一方面这意味着它们可以使您的进程 运行进入系统限制。