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)
其实都在文档里写着了
第一个 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(’’)也会泄漏呢?
非常感谢
@koroshi 看不懂 😓
@xudeming208 没人说过 new array 只会使用 new space 的空间啊。。。还有你把 semi space 设成这么大,你的进程就完蛋了。。。 你对内存泄漏的含义可能有误区,new 一个很大的 array 并不是泄漏,是你操作需要的内存超过了堆限制而已
@hyj1991 感谢大神 是的,semi space不能设置过大,我开始以为设置的是old space
> 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了,所以报错的,我这样理解对吗?