在 Node.js 中(如果您想知道:v16.15.0):
我想一遍又一遍地创建全局变量,直到我的计算机的堆栈/堆没有更多空间,并且出现错误。我认为以下代码可以做到这一点:
for (let i = 0; true; i++) {
eval('var a' + i + ' = "I will break you."')
}
但是当我运行代码时,我等了几分钟,它继续运行,显然没有破坏我的记忆。为什么它不会导致我的内存溢出,我该如何修改它来实现这一点?
附言- 我知道在循环中用
let
声明变量会使变量作用域限定在该块内,但我认为 var
在这种情况下是全局作用域。显然不是?
我认为你有一个特殊的测试,强调 v8 分配器/垃圾收集器,导致它在耗尽内存堆之前卡住。
稍微修改您的循环,并使用
--trace-gc
运行节点突出显示进度停止的地方。
for (let i = 1; true; i++) {
if (i%10000 === 0) console.log(i)
eval(`var a${i} = "I will break you."`)
}
在我的本地节点上,在 8388608 个变量之后,循环不会进行太多,因为每次分配(即迭代)都会触发 GC。
8388601
8388602
8388603
8388604
8388605
8388606
8388607
8388608
8388609
[97743:0x7f9c58078000] 97804 ms: Scavenge 2062.5 (2831.9) -> 1995.0 (2767.9) MB, 5.4 / 0.0 ms (average mu = 0.896, current mu = 0.827) allocation failure;
8388610
[97743:0x7f9c58078000] 100681 ms: Scavenge 2059.0 (2831.9) -> 1994.9 (2767.9) MB, 2.5 / 0.0 ms (average mu = 0.896, current mu = 0.827) allocation failure;
8388611
[97743:0x7f9c58078000] 103477 ms: Scavenge 2058.9 (2831.9) -> 1994.9 (2736.9) MB, 1.5 / 0.0 ms (average mu = 0.896, current mu = 0.827) allocation failure;
8388612
[97743:0x7f9c58078000] 106332 ms: Scavenge 2058.9 (2800.9) -> 1994.9 (2736.9) MB, 1.9 / 0.0 ms (average mu = 0.896, current mu = 0.827) allocation failure;
8388613
[97743:0x7f9c58078000] 109183 ms: Scavenge 2058.9 (2800.9) -> 1994.9 (2736.9) MB, 1.4 / 0.0 ms (average mu = 0.896, current mu = 0.827) allocation failure;
耗尽堆的更快方法是避免
eval
并将引用存储到更长、不同的字符串。
let a = {}
for (let i = 0; true; i++) {
a[i] = "I will break you."+i+i+i+i
}
% node --trace-gc break.js
[97778:0x7faec0078000] 30 ms: Scavenge 6.0 (6.3) -> 5.8 (7.3) MB, 1.2 / 0.0 ms (average mu = 1.000, current mu = 1.000) allocation failure;
[97778:0x7faec0078000] 31 ms: Scavenge 6.0 (7.3) -> 5.9 (8.1) MB, 0.8 / 0.0 ms (average mu = 1.000, current mu = 1.000) allocation failure;
[97778:0x7faec0078000] 32 ms: Scavenge 6.7 (8.1) -> 6.8 (10.6) MB, 0.6 / 0.0 ms (average mu = 1.000, current mu = 1.000) allocation failure;
[97778:0x7faec0078000] 36 ms: Scavenge 8.0 (10.8) -> 7.8 (11.5) MB, 1.6 / 0.0 ms (average mu = 1.000, current mu = 1.000) allocation failure;
[97778:0x7faec0078000] 38 ms: Scavenge 8.7 (11.5) -> 8.8 (16.5) MB, 0.9 / 0.0 ms (average mu = 1.000, current mu = 1.000) allocation failure;
<snip>
[97778:0x7faec0078000] 29994 ms: Scavenge 3990.3 (4071.6) -> 3991.9 (4073.1) MB, 10.7 / 0.0 ms (average mu = 0.222, current mu = 0.199) allocation failure;
[97778:0x7faec0078000] 30019 ms: Scavenge 3992.0 (4073.1) -> 3990.3 (4087.6) MB, 24.2 / 0.0 ms (average mu = 0.222, current mu = 0.199) allocation failure;
[97778:0x7faec0078000] 30037 ms: Scavenge 4006.0 (4087.6) -> 4007.7 (4089.1) MB, 10.0 / 0.0 ms (average mu = 0.222, current mu = 0.199) allocation failure;
[97778:0x7faec0078000] 30058 ms: Scavenge 4007.7 (4089.1) -> 4006.0 (4103.1) MB, 21.6 / 0.0 ms (average mu = 0.222, current mu = 0.199) allocation failure;
<--- Last few GCs --->
[97778:0x7faec0078000] 30019 ms: Scavenge 3992.0 (4073.1) -> 3990.3 (4087.6) MB, 24.2 / 0.0 ms (average mu = 0.222, current mu = 0.199) allocation failure;
[97778:0x7faec0078000] 30037 ms: Scavenge 4006.0 (4087.6) -> 4007.7 (4089.1) MB, 10.0 / 0.0 ms (average mu = 0.222, current mu = 0.199) allocation failure;
[97778:0x7faec0078000] 30058 ms: Scavenge 4007.7 (4089.1) -> 4006.0 (4103.1) MB, 21.6 / 0.0 ms (average mu = 0.222, current mu = 0.199) allocation failure;
<--- JS stacktrace --->
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
1: 0x101705fb5 node::Abort() (.cold.1) [/node-18.15.0/bin/node]
2: 0x100186a29 node::Abort() [/node-18.15.0/bin/node]
3: 0x100186c0e node::OOMErrorHandler(char const*, bool) [/node-18.15.0/bin/node]
4: 0x100315cc3 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [/node-18.15.0/bin/node]
5: 0x1004de975 v8::internal::Heap::FatalProcessOutOfMemory(char const*) [/node-18.15.0/bin/node]
6: 0x1004e2e40 v8::internal::Heap::CollectSharedGarbage(v8::internal::GarbageCollectionReason) [/node-18.15.0/bin/node]
7: 0x1004df68f v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::internal::GarbageCollectionReason, char const*, v8::GCCallbackFlags) [/node-18.15.0/bin/node]
8: 0x1004dc768 v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [/node-18.15.0/bin/node]
9: 0x1004db762 v8::internal::Heap::HandleGCRequest() [/node-18.15.0/bin/node]
10: 0x10047c681 v8::internal::StackGuard::HandleInterrupts() [/node-18.15.0/bin/node]
11: 0x1008e7bf8 v8::internal::Runtime_StackGuard(int, unsigned long*, v8::internal::Isolate*) [/node-18.15.0/bin/node]
12: 0x100cdddb9 Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit [/node-18.15.0/bin/node]
13: 0x105a8cc08