V8的内存限制
发布于 1 天前 作者 xudeming208 578 次浏览 来自 问答

V8的内存限制

  • V8把内存分为「新生代(New Space)」和 「老生代 (Old Space)」。新生代中的对象为存活时间较短的对象,老生代中的对象为存活时间较长或常驻内存的对象;–max_old_space_size改变的是老生代的内存大小,单位为M;–max_new_space_size改变的是新生代的内存大小,单位为K;
  • 《深入浅出nodejs》书中“内存控制章节”说明,64位系统约为1.4GB,32位系统约为0.7GB;
  • 书中说明,Buffer是基于c++,其内存是Node的C++层面提供的,不受V8内存限制;
  • 我的测试结果:Buffer的内存不能大于2G,否则会内存泄漏,Array不能大于4G,这点我不太明白;为什么Buffer会有内存限制呢?而Array的限制却为4G,不应该是1.4G么?测试代码如下:
// 2G,会报错:RangeError: Invalid typed array length
new Buffer(2*1024*1024*1024)
// 2G-1,正常,不会报错
new Buffer(2*1024*1024*1024 - 1)
// 4G,会报错:RangeError: Invalid typed array length
new Array(4*1024*1024*1024)
// 4G-1,正常,不会报错
new Array(4*1024*1024*1024-1)
18 回复

其实都在文档里写着了 第一个 Buffer >= 2^31 抛错是 node 的一些限制:require('buffer').constants 可以看到:{ MAX_LENGTH: 2147483647, MAX_STRING_LENGTH: 268435440 },这个值也会变,64 位系统下限制是 2^31 -1,32 位系统下限制是 2^30 -1

第二个 new Array >= 2^32 抛错的原因可以看这里:

What went wrong? An invalid array length might appear in these situations:

  • When creating an Array or an ArrayBuffer which has a length which is either negative or larger or equal to 2^32.
  • when setting the Array.length property to a value which is either negative or larger or equal to 2^32.

Why are Array and ArrayBuffer length limited? The length property of an Array or an ArrayBuffer is represented with an unsigned 32-bit integer, that can only store values which are in the range from 0 to 2^32-1.

另外,如果你在下面申请的 array 赋任意值就会因为申请内存超过 1.4G 导致 OOM 了,比如: new Array(4*1024*1024*1024-1).fill('*')

最后,目前更改 new space 的 falg 改为: --max_semi_space_size 了哦,new space 的值 = 2 * max_semi_space_size

参考:

@hyj1991 大神,请问new Array(4102410241024-1).fill(’’)给数组填充后为什么就会OOM呢,空的数组就不会呢,两者对内容的申请大小是不一样的吗?另外最后new space 的值 = 2 * max_semi_space_size是什么意思呀?谢谢

@xudeming208 只是 new Array 的话并没有实际地去申请内存,填充内容的时候才真的去申请内存并存储值了; new space 采用的是 scavenge 算法,这个算法是空间换时间的——new space 空间分为两个相等大小的 semi_space,同一时刻只有其中的一半在使用,所以你设置的 --max_semi_space_size 其实就是真正会使用到的大小,但是实际上真正的 new space 的大小 = 2 * max_semi_space_size

@hyj1991 new Array(parseInt(1024 * 1024 * 1024 * 1.4)).fill(’’)也会泄漏呢(node --max_semi_space_size=5000一样泄漏),通过查看知道直接new Array是只用了new space,资料说new space 64位系统为32M * 2,那为什么我new Array(1024 * 1024 * 32).fill(’’)也会泄漏呢?

@hyj1991 大神,我生产环境30多qps就显示cpugc飙高15~20多左右,看cpuprofile发现是monggose内部对象序列化的问题,我想请求在不修改代码的情况下,通过配置–max_semi_space_size或者old能不能降低cpu飙升情况?

@koroshi –max_semi_space_size或者old只是改变内存问题,cpu飙升是计算太多类的,这是两个问题吧

@xudeming208 我是想能不能通过提升默认内存减少gc次数提高计算问题?

@koroshi V8的old space用的是增量标记,gc应该不会太大占用CPU吧,而且,申请的内存越大,遍历gc的时候花的时间不是更长吗?不是更占CPU吗?请赐教,谢谢

@xudeming208 我刚开始看这块,我遇到的问题就是gc太高了,但是代码暂时还没法重构,就想查查有没有通过配置提高一点负荷,因为在机器cpu裱满的时候内存一直不吃紧,所以想有没有什么办法空间换时间提高一点性能,我有一点alinode那天的情况可以开个帖子请教请教

@koroshi 请教怎么查看gc太高了?

@xudeming208 我是alinode采集到的,不知道能否看看分析一下 https://cnodejs.org/topic/5ae036a3a86ec1f308ec250b

@xudeming208 接入性能平台看看呢 Node.js 性能平台 一般来说调整 max_semi_space_size 或者 max_old_space-size 是没办法优化 cpu 的序列化操作的

@hyj1991 方不方便帮忙看一下图表,是不是只能通过重构减少数据库查询优化了 https://cnodejs.org/topic/5ae036a3a86ec1f308ec250b

@hyj1991 好的,还麻烦大神帮忙赐教一下: new Array(parseInt(1024 * 1024 * 1024 * 1.4)).fill(’’)也会泄漏呢(node --max_semi_space_size=5000一样泄漏),资料说new space 64位系统为32M * 2,那为什么我new Array(1024 * 1024 * 32).fill(’’)也会泄漏呢?

非常感谢

@xudeming208 没人说过 new array 只会使用 new space 的空间啊。。。还有你把 semi space 设成这么大,你的进程就完蛋了。。。 你对内存泄漏的含义可能有误区,new 一个很大的 array 并不是泄漏,是你操作需要的内存超过了堆限制而已

@hyj1991 感谢大神 是的,semi space不能设置过大,我开始以为设置的是old space

@hyj1991

> new Array(parseInt(1024 * 1024 * 32)).fill('')

<--- Last few GCs --->

[38037:0x103800000]    48108 ms: Mark-sweep 1399.6 (1420.5) -> 1399.6 (1420.5) MB, 1611.4 / 0.0 ms  allocation failure GC in old space requested
[38037:0x103800000]    49593 ms: Mark-sweep 1399.6 (1420.5) -> 1399.6 (1419.5) MB, 1484.7 / 0.0 ms  last resort
[38037:0x103800000]    50992 ms: Mark-sweep 1399.6 (1419.5) -> 1399.6 (1419.5) MB, 1399.0 / 0.0 ms  last resort


<--- JS stacktrace --->

==== JS stack trace =========================================

Security context: 0x22262a7a66a1 <JS Object>
    0: builtin exit frame: keys(this=0x22262a783f31 <JS Function Object (SharedFunctionInfo 0x20080a98bad1)>,0x3950ca79cf61 <JS Array[33554432]>)

    1: formatValue(aka formatValue) [util.js:373] [pc=0x9a04072a051](this=0x20080a982311 <undefined>,ctx=0x3950ca79d2f9 <an Object with map 0x241b8fdbdc41>,value=0x3950ca79cf61 <JS Array[33554432]>,recurseTimes=2)
    2: inspect [util.js:187] [pc=0x9...

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory
 1: node::Abort() [/usr/local/bin/node]
 2: node::FatalException(v8::Isolate*, v8::Local<v8::Value>, v8::Local<v8::Message>) [/usr/local/bin/node]
 3: v8::internal::V8::FatalProcessOutOfMemory(char const*, bool) [/usr/local/bin/node]
 4: v8::internal::Factory::NewRawOneByteString(int, v8::internal::PretenureFlag) [/usr/local/bin/node]
 5: v8::internal::Factory::NumberToString(v8::internal::Handle<v8::internal::Object>, bool) [/usr/local/bin/node]
 6: v8::internal::(anonymous namespace)::ElementsAccessorBase<v8::internal::(anonymous namespace)::FastHoleyObjectElementsAccessor, v8::internal::(anonymous namespace)::ElementsKindTraits<(v8::internal::ElementsKind)3> >::DirectCollectElementIndicesImpl(v8::internal::Isolate*, v8::internal::Handle<v8::internal::JSObject>, v8::internal::Handle<v8::internal::FixedArrayBase>, v8::internal::GetKeysConversion, v8::internal::PropertyFilter, v8::internal::Handle<v8::internal::FixedArray>, unsigned int*, unsigned int) [/usr/local/bin/node]
 7: v8::internal::(anonymous namespace)::ElementsAccessorBase<v8::internal::(anonymous namespace)::FastHoleyObjectElementsAccessor, v8::internal::(anonymous namespace)::ElementsKindTraits<(v8::internal::ElementsKind)3> >::PrependElementIndices(v8::internal::Handle<v8::internal::JSObject>, v8::internal::Handle<v8::internal::FixedArrayBase>, v8::internal::Handle<v8::internal::FixedArray>, v8::internal::GetKeysConversion, v8::internal::PropertyFilter) [/usr/local/bin/node]
 8: v8::internal::FastKeyAccumulator::GetKeysFast(v8::internal::GetKeysConversion) [/usr/local/bin/node]
 9: v8::internal::FastKeyAccumulator::GetKeys(v8::internal::GetKeysConversion) [/usr/local/bin/node]
10: v8::internal::KeyAccumulator::GetKeys(v8::internal::Handle<v8::internal::JSReceiver>, v8::internal::KeyCollectionMode, v8::internal::PropertyFilter, v8::internal::GetKeysConversion, bool) [/usr/local/bin/node]
11: v8::internal::Builtin_Impl_ObjectKeys(v8::internal::BuiltinArguments, v8::internal::Isolate*) [/usr/local/bin/node]
12: 0x9a040584167
13: 0x9a04072a051
14: 0x9a0407296c0
Abort trap: 6

看这个log,老生代接近1.4G了,但是申请的Array大小只有32M,所以不可能是存储Array,导致的泄漏,这个报错是因为操作Array(fill)时需要的内存要超过1.4G了,所以报错的,我这样理解对吗?

回到顶部