使用G1收集器,可以将一个数组对象分配给不同的对象吗



//VM参数:-verbose:gc-Xms20M-Xmx20M-Xmn10M-XXX:+PrintGCDetails-XXX:SivitorRatio=8-XX:+UseG1GC

public static void testAllocation() {
byte[] allocation1, allocation2, allocation3, allocation4;
allocation1 = new byte[2 * _1MB];
allocation2 = new byte[2 * _1MB];
allocation3 = new byte[2 * _1MB];
allocation4 = new byte[4 * _1MB];
}
public static void main(String[] args) {
testAllocation();
}

运行程序我得到以下结果:

[GC暂停(G1 Humongous分配((年轻((初始标记(,0.0015208秒]……

堆垃圾第一堆总计20480K,已使用10816K[0x00000007bec00000,0x00000007bed000a0,0x00000007c0000000(区域大小1024K,2名年轻人(2048K(,1名幸存者(1024K(元空间已使用3308K,容量4496K,已提交4864K,已保留1056768K类空间已使用365K,容量388K,承诺512K,保留1048576K

================================================

阵列大小是2MB或4MB,区域大小是1MB,有人能告诉我为什么它使用了2个年轻区域和1个幸存者吗?

首先,您正在指定-XX:SurvivorRatio=8,但-XX:+UseAdaptiveSizePolicy处于启用状态,因此SurvivorRatio将被忽略。

然后,我解释了你在这个答案中看到的一些线条。

无论如何,我用java-13和统一日志运行了这个,特别是用:

"-Xlog:heap*=debug" "-Xlog:gc*=debug"

看看发生了什么。

从日志中,我看到只有两个GC周期,作为此代码的一部分实际上这只是一个并发循环,但作为该循环的一部分,年轻GC也会被触发。从逻辑上讲,我将其视为一个GC周期,但GC日志报告为两个。

因此,您的日志将包含:

[0.127s][debug][gc,ergo] Request concurrent cycle initiation (requested by GC cause). GC cause: G1 Humongous Allocation

此外,您将拥有:

[0.133s][info ][gc    ] GC(0) Pause Young (Concurrent Start) (G1 Humongous Allocation) 7M->6M(20M) 5.753ms
[0.133s][info ][gc,cpu] GC(0) User=0.01s Sys=0.00s Real=0.01s
[0.133s][info ][gc    ] GC(1) Concurrent Cycle

请注意并发循环是如何从年轻的循环中剥离出来的。


现在,回答有关区域的问题。

GC (0)以开头

[0.127s][debug][gc,heap] GC(0) Heap before GC invocations=0 (full 0)...
[0.127s][debug][gc,heap] GC(0) region size 1024K, 1 young, 0 survivors

所以你从1 Young = 1 Eden + 0 Survivors开始

稍后,会有一个日志:

[0.133s][info ][gc,heap] GC(0) Eden regions:     1 -> 0 (9)
[0.133s][info ][gc,heap] GC(0) Survivor regions: 0 -> 1 (2)

正如我在上面给出的链接中所解释的,年轻GC的下一个循环将被提示使用:

11 young regions = 9 Eden + 2 Survivor

但是,正如我在那里所说,这只是一个暗示。由于这是一个巨大的分配,因此当您退出时,它实际上会使用更少的区域,如堆布局所示:

[0.157s][info ][gc,heap,exit]  garbage-first heap   total 20480K....
[0.158s][info ][gc,heap,exit]  region size 1024K, 2 young 1 survivors

GC不知道你只分配大对象,它根本不应该使用任何年轻区域,它的启发式仍然可以创建一些年轻区域。这就是为什么你会看到那些2 young, 1 survivor

但是,如果你运行代码的时间足够长:

public static void main(String[] args) {
while (true) {
invokeMe();
}
}
public static int invokeMe() {
int x = 1024;
int factor = 2;
byte[] allocation1 = new byte[factor * x * x];
allocation1[2] = 3;
byte[] allocation2 = new byte[factor * x * x];
byte[] allocation3 = new byte[factor * x * x];
byte[] allocation4 = new byte[factor * factor * x * x];
return Arrays.hashCode(allocation1) ^ Arrays.hashCode(allocation2)
^ Arrays.hashCode(allocation3) ^ Arrays.hashCode(allocation4);
}

您将开始看到以下条目:

[0.521s][debug][gc,heap] GC(62) Heap before GC invocations=62 
[0.521s][debug][gc,heap] GC(62) region size 1024K, 0 young (0K), 0 survivors (0K)

注意0 young, 0 survivor

最新更新