我想一遍又一遍地创建全局变量,直到我的计算机堆没有更多空间,然后出现错误。我认为以下代码可以做到这一点:
for (let i = 0; true; i++) {
eval('var a' + i + ' = "I will break you."')
}
但是当我运行代码时,我等了几分钟,它继续运行,显然没有破坏我的记忆(我认为我的程序会终止并显示一条错误消息,类似于发生堆栈溢出时)。为什么它不会导致我的记忆中断,我该如何修改它来实现这一点?
这是与 Node.js 一起运行的,版本
16.15.0
.
附言- 我知道在循环中用
let
声明一个变量会使变量作用于该块,但我认为 var
在这种情况下是全局作用域,在整个程序中创建许多变量实例。
我认为你有一个特殊的测试,强调 v8 分配器/垃圾收集器并导致它在 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 个变量(这是一个可疑的 8192*1024 轮)之后,循环不会进行太多,因为每次分配都会触发 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
并存储对更长的连接字符串的引用,以避免 v8 能够从代码中引用内部化的字符串内存。
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