V8 GC何时触发其第一个小GC循环(清除)



我正在努力理解这个分配1兆字节的简单代码:

var BYTES_IN_MB = 1024 * 1024; // 1MB = 1024KB = 1024(KB/MB)*1024(B/KB)
var BYTES_IN_SMI = 4;
var NUM_SMIS_IN_MB = BYTES_IN_MB/BYTES_IN_SMI;
var y = [];
function allocateMB() {
  for(var i = 0; i < NUM_SMIS_IN_MB; i++) {
    y.push(i);
  }
}
allocateMB();

导致4个小的gc循环(清除)。正如您所看到的,我们明确地将新空间的大小设置为1兆字节。预期的结果是看到一个拾荒者,但我不明白为什么我们看到四个。

➜  ~  d8
V8 version 4.5.103.35 [console: readline]
➜  ~  d8 --trace_gc --trace_gc_verbose  --min_semi_space_size=1 --max_semi_space_size=1 --target_semi_space_size=1 --semi_space_growth_factor=0 allocateMB.js
[2853:0x7fa1fa012600]        6 ms: Scavenge 1.7 (36.9) -> 1.2 (36.9) MB, 0.3 / 0 ms [allocation failure].
[2853:0x7fa1fa012600] Memory allocator,   used:  37752 KB, available: 1461384 KB
[2853:0x7fa1fa012600] New space,          used:    281 KB, available:    726 KB, committed:   2015 KB
[2853:0x7fa1fa012600] Old space,          used:    674 KB, available:      0 KB, committed:    794 KB
[2853:0x7fa1fa012600] Code space,         used:    194 KB, available:      0 KB, committed:    261 KB
[2853:0x7fa1fa012600] Map space,          used:     34 KB, available:      0 KB, committed:     59 KB
[2853:0x7fa1fa012600] Large object space, used:      0 KB, available: 1460343 KB, committed:      0 KB
[2853:0x7fa1fa012600] All spaces,         used:   1185 KB, available: 1461069 KB, committed:   3130 KB
[2853:0x7fa1fa012600] External memory reported:      0 KB
[2853:0x7fa1fa012600] Total time spent in GC  : 0.3 ms
[2853:0x7fa1fa012600]        7 ms: Scavenge 1.5 (36.9) -> 1.3 (36.9) MB, 0.4 / 0 ms [allocation failure].
[2853:0x7fa1fa012600] Memory allocator,   used:  37752 KB, available: 1461384 KB
[2853:0x7fa1fa012600] New space,          used:    397 KB, available:    610 KB, committed:   2015 KB
[2853:0x7fa1fa012600] Old space,          used:    691 KB, available:      0 KB, committed:    794 KB
[2853:0x7fa1fa012600] Code space,         used:    194 KB, available:      0 KB, committed:    261 KB
[2853:0x7fa1fa012600] Map space,          used:     34 KB, available:      0 KB, committed:     59 KB
[2853:0x7fa1fa012600] Large object space, used:      0 KB, available: 1460343 KB, committed:      0 KB
[2853:0x7fa1fa012600] All spaces,         used:   1317 KB, available: 1460954 KB, committed:   3130 KB
[2853:0x7fa1fa012600] External memory reported:      0 KB
[2853:0x7fa1fa012600] Total time spent in GC  : 0.7 ms
[2853:0x7fa1fa012600]        8 ms: Scavenge 1.9 (36.9) -> 1.5 (36.9) MB, 0.2 / 0 ms [allocation failure].
[2853:0x7fa1fa012600] Memory allocator,   used:  37752 KB, available: 1461384 KB
[2853:0x7fa1fa012600] New space,          used:    596 KB, available:    411 KB, committed:   2015 KB
[2853:0x7fa1fa012600] Old space,          used:    691 KB, available:      0 KB, committed:    794 KB
[2853:0x7fa1fa012600] Code space,         used:    194 KB, available:      0 KB, committed:    261 KB
[2853:0x7fa1fa012600] Map space,          used:     34 KB, available:      0 KB, committed:     59 KB
[2853:0x7fa1fa012600] Large object space, used:      0 KB, available: 1460343 KB, committed:      0 KB
[2853:0x7fa1fa012600] All spaces,         used:   1516 KB, available: 1460755 KB, committed:   3130 KB
[2853:0x7fa1fa012600] External memory reported:      0 KB
[2853:0x7fa1fa012600] Total time spent in GC  : 0.9 ms
[2853] Limited new space size due to high promotion rate: 1 MB
[2853:0x7fa1fa012600]        8 ms: Scavenge 1.5 (36.9) -> 1.5 (37.9) MB, 0.7 / 0 ms [allocation failure].
[2853:0x7fa1fa012600] Memory allocator,   used:  38776 KB, available: 1460360 KB
[2853:0x7fa1fa012600] New space,          used:      0 KB, available:   1007 KB, committed:   2015 KB
[2853:0x7fa1fa012600] Old space,          used:   1287 KB, available:    102 KB, committed:   1801 KB
[2853:0x7fa1fa012600] Code space,         used:    194 KB, available:      0 KB, committed:    261 KB
[2853:0x7fa1fa012600] Map space,          used:     34 KB, available:      0 KB, committed:     59 KB
[2853:0x7fa1fa012600] Large object space, used:      0 KB, available: 1459319 KB, committed:      0 KB
[2853:0x7fa1fa012600] All spaces,         used:   1516 KB, available: 1460430 KB, committed:   4138 KB
[2853:0x7fa1fa012600] External memory reported:      0 KB
[2853:0x7fa1fa012600] Total time spent in GC  : 1.6 ms

新空间似乎使用了半空间分配策略(请参阅"Deep Dive…"下),因此它不一定是1MB的确切限制。事实上,似乎最多允许80%的新空间被填满。

还有一种机制可以通过更改分配限制来确保快速清除(请参阅80%链接中的第55行)。因此GC的行为并不像看上去那么简单,但阅读V8源代码和注释会给出很好的提示。

"allocateMb"显示了对数组大小的不完全理解。数组大小从4开始,每次需要调整大小时都会使用x1.5 + 16来增加大小。所以你实际上是这样调整数组的大小:

4      (garbage so far: 4 * 4 + 1 * 2 * 4 =       24 bytes [48 in 64-bit])
22     (garbage so far: 26 * 4 + 2 * 2 * 4 =      120 bytes [240 in 64-bit])
49     (garbage so far: 75 * 4 + 3 * 2 * 4 =      324 bytes [648 in 64-bit])
89     (garbage so far: 164 * 4 + 4 * 2 * 4 =     688 bytes [1376 in 64-bit])
149    (garbage so far: 313 * 4 + 5 * 2 * 4 =     1292 bytes [2584 in 64-bit])
239    (garbage so far: 552 * 4 + 6 * 2 * 4 =     2256 bytes [4512 in 64-bit])
374    (garbage so far: 926 * 4 + 7 * 2 * 4 =     3760 bytes [7520 in 64-bit])
577    (garbage so far: 1503 * 4 + 8 * 2 * 4 =    6076 bytes [12152 in 64-bit])
881    (garbage so far: 2384 * 4 + 9 * 2 * 4 =    9608 bytes [19216 in 64-bit])
1337   (garbage so far: 3721 * 4 + 10 * 2 * 4 =   14964 bytes [29928 in 64-bit])
2021   (garbage so far: 5742 * 4 + 11 * 2 * 4 =   23056 bytes [46112 in 64-bit])
3047   (garbage so far: 8789 * 4 + 12 * 2 * 4 =   35252 bytes [70504 in 64-bit])
4586   (garbage so far: 13375 * 4 + 13 * 2 * 4 =  53604 bytes [107208 in 64-bit])
6895   (garbage so far: 20270 * 4 + 14 * 2 * 4 =  81192 bytes [162384 in 64-bit])
10358  (garbage so far: 30628 * 4 + 15 * 2 * 4 =  122632 bytes [245264 in 64-bit])
15553  (garbage so far: 46181 * 4 + 16 * 2 * 4 =  184852 bytes [369704 in 64-bit])
23345  (garbage so far: 69526 * 4 + 17 * 2 * 4 =  278240 bytes [556480 in 64-bit])
35033  (garbage so far: 104559 * 4 + 18 * 2 * 4 = 418380 bytes [836760 in 64-bit])
52565  (garbage so far: 157124 * 4 + 19 * 2 * 4 = 628648 bytes [1257296 in 64-bit])
78863  (garbage so far: 235987 * 4 + 20 * 2 * 4 = 944108 bytes [1888216 in 64-bit])
118310 (garbage so far: 354297 * 4 + 21 * 2 * 4 = 1417356 bytes [2834712 in 64-bit])
177481 (garbage so far: 531778 * 4 + 22 * 2 * 4 = 2127288 bytes [4254576 in 64-bit])
266237 (not garbage)

因此,在达到足够大的266237之前,这是64位中相当于531778 * 4 + 22 * 2 * 4 = 2127288字节或4254576字节的FixedArray垃圾。此外,177481的最后一个垃圾数组甚至无法在新空间中分配,因为它在64位中占用了1419864个字节。

对于大小高达10万个项目的较小阵列,您可以通过执行new Array(size)来跳过浪费的大小调整。

我对未知数组大小使用的一个技巧是给出近似值,然后重置长度:

var myArray = new Array(1024)
myArray.length = 0;
// Can now use .push without having to resize the backing array

最新更新