运行大型 Jest 测试套件时 Node.js 内存不足

Ben*_*gel 7 node.js jestjs react-testing-library

我\xe2\x80\x99m在 Node.js ( ) 上使用 Jest ( 26.6.2) 和 React-testing-library ( ) 运行一个大型测试套件。12.1.216.3.1

\n

在某些时候,测试套件有时会崩溃

\n
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory\n
Run Code Online (Sandbox Code Playgroud)\n

第一个想法是 Node.js 堆已满。因此,我\xe2\x80\x99通过设置了2GB的堆大小--max-old-space-size=2048,并启用了GC日志记录(Node\xe2\x80\x99s --expose-gc+ Jest\xe2\x80\x99s --logHeapUsage)。\nGC日志记录表明堆空间可能是不是问题(如果我的理解是正确的,错误消息至少具有误导性):

\n
<--- Last few GCs --->\n\n[206149:0x4b56570]  1113100 ms: Mark-sweep (reduce) 872.1 (1175.1) -> 871.2 (1114.6) MB, 1786.2 / 0.0 ms  (average mu = 0.018, current mu = 0.014) last resort GC in old space requested\n[206149:0x4b56570]  1114841 ms: Mark-sweep (reduce) 871.2 (1114.6) -> 871.2 (1091.4) MB, 1740.9 / 0.0 ms  (average mu = 0.009, current mu = 0.000) last resort GC in old space requested\n\n\n<--- JS stacktrace --->\n\nFATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory\n  1: 0xb00d90 node::Abort() [./.gradle/nodejs/node-v16.13.1-linux-x64/bin/node]\n  2: 0xa1823b node::FatalError(char const*, char const*) [./.gradle/nodejs/node-v16.13.1-linux-x64/bin/node]\n  3: 0xcedbce v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [./.gradle/nodejs/node-v16.13.1-linux-x64/bin/node]\n  4: 0xcedf47 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [./.gradle/nodejs/node-v16.13.1-linux-x64/bin/node]\n  5: 0xea6105  [./.gradle/nodejs/node-v16.13.1-linux-x64/bin/node]\n  6: 0xeb8588 v8::internal::Heap::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [./.gradle/nodejs/node-v16.13.1-linux-x64/bin/node]\n  7: 0xe85b76 v8::internal::Factory::CodeBuilder::AllocateCode(bool) [./.gradle/nodejs/node-v16.13.1-linux-x64/bin/node]\n  8: 0xe86885 v8::internal::Factory::CodeBuilder::BuildInternal(bool) [./.gradle/nodejs/node-v16.13.1-linux-x64/bin/node]\n  9: 0xe8729e v8::internal::Factory::CodeBuilder::Build() [./.gradle/nodejs/node-v16.13.1-linux-x64/bin/node]\n 10: 0x14d69fe v8::internal::RegExpMacroAssemblerX64::GetCode(v8::internal::Handle<v8::internal::String>) [./.gradle/nodejs/node-v16.13.1-linux-x64/bin/node]\n 11: 0x11c1329 v8::internal::RegExpCompiler::Assemble(v8::internal::Isolate*, v8::internal::RegExpMacroAssembler*, v8::internal::RegExpNode*, int, v8::internal::Handle<v8::internal::String>) [./.gradle/nodejs/node-v16.13.1-linux-x64/bin/node]\n 12: 0x11df6b7 v8::internal::RegExpImpl::Compile(v8::internal::Isolate*, v8::internal::Zone*, v8::internal::RegExpCompileData*, v8::base::Flags<v8::internal::JSRegExp::Flag, int>, v8::internal::Handle<v8::internal::String>, v8::internal::Handle<v8::internal::String>, bool, unsigned int&) [./.gradle/nodejs/node-v16.13.1-linux-x64/bin/node]\n 13: 0x11dfe60 v8::internal::RegExpImpl::CompileIrregexp(v8::internal::Isolate*, v8::internal::Handle<v8::internal::JSRegExp>, v8::internal::Handle<v8::internal::String>, bool) [./.gradle/nodejs/node-v16.13.1-linux-x64/bin/node]\n 14: 0x11e0a5e v8::internal::RegExpImpl::IrregexpPrepare(v8::internal::Isolate*, v8::internal::Handle<v8::internal::JSRegExp>, v8::internal::Handle<v8::internal::String>) [./.gradle/nodejs/node-v16.13.1-linux-x64/bin/node]\n 15: 0x11e0bd7 v8::internal::RegExpImpl::IrregexpExec(v8::internal::Isolate*, v8::internal::Handle<v8::internal::JSRegExp>, v8::internal::Handle<v8::internal::String>, int, v8::internal::Handle<v8::internal::RegExpMatchInfo>, v8::internal::RegExp::ExecQuirks) [./.gradle/nodejs/node-v16.13.1-linux-x64/bin/node]\n 16: 0x1205da8 v8::internal::Runtime_RegExpExec(int, unsigned long*, v8::internal::Isolate*) [./.gradle/nodejs/node-v16.13.1-linux-x64/bin/node]\n 17: 0x15e7819  [./.gradle/nodejs/node-v16.13.1-linux-x64/bin/node]\n
Run Code Online (Sandbox Code Playgroud)\n

测试在不同点(甚至不同的测试类!)崩溃,但我总是RegExp在 JS 堆栈跟踪中看到编译。搜索 stacktrace 模式将我带到https://bugs.chromium.org/p/v8/issues/detail?id=10441#c5,它给出了 V8 的提示code_space内存区域可能是问题所在:

\n
\n

V8 对代码对象的限制为 128*MB。每个 RegExp.exec(或 String.match)都会编译一个表达式,从而创建一个代码对象,该对象可能很大,具体取决于 RegExp。

\n
\n

事实上,当我通过添加 V8 堆空间统计信息的日志记录时v8.getHeapSpaceStatistics(),我可以看到该code_space区域可能导致崩溃。\n崩溃前我看到的最后一个输出是:

\n
Heap statistics: {\n    "total_heap_size": 1251897344,\n    "total_heap_size_executable": 133742592,\n    "total_physical_size": 1249687440,\n    "total_available_size": 1200419000,\n    "used_heap_size": 968511640,\n    "heap_size_limit": 2197815296,\n    "malloced_memory": 4202568,\n    "peak_malloced_memory": 12083656,\n    "does_zap_garbage": 0,\n    "number_of_native_contexts": 3,\n    "number_of_detached_contexts": 0\n}\nHeap space statistics: [\n    [\xe2\x80\xa6]\n    {\n        "space_name": "old_space",\n        "space_size": 1011437568,\n        "space_used_size": 766010912,\n        "space_available_size": 226275256,\n        "physical_space_size": 1011428824\n    },\n    {\n        "space_name": "code_space",\n        "space_size": 133275648,\n        "space_used_size": 124423840,\n        "space_available_size": 512352,\n        "physical_space_size": 131074880\n    },\n    [\xe2\x80\xa6]\n]\n
Run Code Online (Sandbox Code Playgroud)\n

我\xe2\x80\x99m 想知道崩溃是否表明测试套件太大或者是否是内存泄漏?

\n

有没有办法增加code_spaceV8中的区域大小?

\n

I\xe2\x80\x99m 感谢任何缩小这些崩溃根本原因范围的提示。

\n