Why can't I exhaust the heap by dynamically creating globally-scoped variables in an infinite loop in JavaScript/Node.js?

huangapple go评论57阅读模式
英文:

Why can't I exhaust the heap by dynamically creating globally-scoped variables in an infinite loop in JavaScript/Node.js?

问题

我想一遍又一遍地创建全局变量,直到我的计算机的堆空间不再有空间,然后出现错误。我认为以下代码可以实现这个目标:

for (let i = 0; true; i++) {
    eval('var a' + i + ' = "I will break you."')
}

但当我运行这段代码时,等待了几分钟,它似乎一直在运行,没有导致内存耗尽(我本来以为程序会因错误消息而终止,类似于堆栈溢出时发生的情况)。为什么它没有导致内存耗尽,以及我应该如何修改它以实现这个目标?

这是在 Node.js 版本 16.15.0 中运行的。

P.S. - 我知道在循环中使用 let 声明变量将使变量作用域限制在该块内,但我认为在这种情况下使用 var 会使变量在整个程序中具有全局作用域,从而在程序中创建多个变量的实例。

英文:

I want to create global variables over and over until my computer's heap has no more space, and I get an error. I thought the following code would work to do so:

for (let i = 0; true; i++) {
    eval('var a' + i + ' = "I will break you."')
}

But when I run the code, I wait a few minutes, and it just keeps going, apparently not breaking my memory (I figured my program would terminate with an error message, similar to when stack overflow happens). Why doesn't it cause my memory to break, and how could I modify it to accomplish this?

This is in run with Node.js, version 16.15.0.

P.S. - I know that declaring a variable with let in a loop makes the variable scoped to that block, but I figured that var was scoped globally in this case, creating many instances of variables throughout the program.

答案1

得分: 4

I think you have a particular test that stresses the v8 allocator/garbage collector and causes it to get stuck before v8 can exhaust the memory heap. Modifying your loop slightly, and running node with --trace-gc highlights where progress grinds to a halt.

for (let i = 1; true; i++) {
  if (i % 10000 === 0) console.log(i)
  eval(`var a${i} = "I will break you."`)
}

On my local node, after 8388608 variables (which is a suspiciously round 8192*1024), the loop doesn't progress much as there is a GC triggered for every allocation (i.e. every iteration of the loop).

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;

A quicker way to exhaust the heap is to avoid the relatively slow eval in the loop and store references to longer, concatenated strings to avoid v8 being able to refer to the internalized string memory from the code.

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]

<details>
<summary>英文:</summary>

I think you have a particular test that stresses the v8 allocator/garbage collector and causes it to get stuck before v8 can exhaust the memory heap. 

Modifying your loop slightly, and running node with `--trace-gc` highlights where progress grinds to a halt. 

for (let i = 1; true; i++) {
if (i%10000 === 0) console.log(i)
eval(var a${i} = &quot;I will break you.&quot;)
}


On my local node, after 8388608 variables (which is a suspiciously round 8192*1024) the loop doesn&#39;t progress much as there is a GC triggered for every allocation (i.e. every iteration of the loop). 

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;


A quicker way to exhaust the heap is to avoid the relatively slow `eval` in the loop and store references to longer, concatenated strings to avoid v8 being able to refer to the internalised string memory from the code.

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


</details>

huangapple
  • 本文由 发表于 2023年4月4日 05:19:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/75923853.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定