一、Minor GC触发条件
1、eden区满时,触发MinorGC。即申请一个对象时,发现eden区不够用,则触发一次MinorGC。
注:新生代分为三个区域,eden space, from space, to space。默认比例是8:1:1。在MinorGC时,会把存活的对象复制到to space区域,如果to space区域不够,则利用担保机制进入老年代区域。
对eden space, from space, to space的理解:每次分配eden space空间,如果不够,则小于 to space大小的对象复制到 to space,然后to space和from space换位置,所以我们看到的to space一直是空的。
测试代码:
private static final int _1M = 1024 * 1024;
private static void testMinorGC() {
/* eden space为8M,from/to space各为1M */
LargeObject largeOb1 = new LargeObject(_1M * 1 / 2, "largeOb1");
LargeObject largeOb2 = new LargeObject(_1M * 1, "largeOb2");
LargeObject largeOb3 = new LargeObject(_1M * 2, "largeOb3");
largeOb3 = null;
LargeObject largeOb4 = new LargeObject(_1M * 3, "largeOb4");
LargeObject largeOb5 = new LargeObject(_1M * 2, "largeOb5");
}
public static void main(String[] agrs) {
testMinorGC();
}
static class LargeObject {
private byte[] data;
private String name;
public LargeObject(int size, String name) {
data = new byte[size];
this.name = name;
System.out.println("Over Constructing LargeObject " + name + System.lineSeparator());
}
public String getName() {
return name;
}
}
测试环境:JDK1.8.0_144,Java HotSpot(TM) 64-Bit Server VM,默认垃圾收集器为PS Scavenge+PS MarkSweep。
-Xms30m -Xmx30m -Xmn10m -XX:+PrintGCDetails -XX:+PrintHeapAtGC
堆总大小为30m,新生代为10m,打印GC信息,在GC前后打印内存信息。在eclipse下运行参考eclipse设置运行JVM参数。
打印结果及分析:
Over Constructing LargeObject largeOb1
Over Constructing LargeObject largeOb2
Over Constructing LargeObject largeOb3
Over Constructing LargeObject largeOb4
{Heap before GC invocations=1 (full 0):
PSYoungGen total 9216K, used 7640K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 93% used [0x00000000ff600000,0x00000000ffd760d8,0x00000000ffe00000)
from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
to space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
ParOldGen total 20480K, used 0K [0x00000000fe200000, 0x00000000ff600000, 0x00000000ff600000)
object space 20480K, 0% used [0x00000000fe200000,0x00000000fe200000,0x00000000ff600000)
Metaspace used 2777K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 299K, capacity 386K, committed 512K, reserved 1048576K
// 实例化largeOb1、largeOb2、largeOb3、largeOb4共使用了eden space 6656K ,从上面信息可以看到eden space还剩余573m,初始化LargeOb5时,需要2048K。
// 由于eden space不够给新的对象分配内存,所以触发一次MinorGC。
// 7640K->1000K(9216K,eden space+from space),新生代GC结果从7640K变成了1000K,这时新生代只存在largeOb1对应的内存,因为largeOb1对应的内存为0.5M,小于to space区大小,进入from space?不太懂。
// largeOb2和largeOb4由于对应的大于to space区大小,担保进入到老年代。
// 7640K->5232K(29696K,eden space+from space+ParOldGen),总的内存占用变化,回收了2408K空间。
// largeOb3之前的内存没有引用(largeOb3=null),所以被回收了。
[GC (Allocation Failure) [PSYoungGen: 7640K->1000K(9216K)] 7640K->5248K(29696K), 0.0030601 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 7640K->616K(9216K)] 7640K->5232K(29696K), 0.0031851 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
// 回收之后的内存情况,可以看到from space存在largeOb1,ParOldGen存在largeOb2和largeOb4。
Heap after GC invocations=1 (full 0):
PSYoungGen total 9216K, used 616K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 0% used [0x00000000ff600000,0x00000000ff600000,0x00000000ffe00000)
from space 1024K, 60% used [0x00000000ffe00000,0x00000000ffe9a020,0x00000000fff00000)
to space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
ParOldGen total 20480K, used 4616K [0x00000000fe200000, 0x00000000ff600000, 0x00000000ff600000)
object space 20480K, 22% used [0x00000000fe200000,0x00000000fe682030,0x00000000ff600000)
Metaspace used 2777K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 299K, capacity 386K, committed 512K, reserved 1048576K
}
Over Constructing LargeObject largeOb5
// 分配完largeOb5,可以看到largeOb5的内存被分配在了新生代的eden space。
Heap
PSYoungGen total 9216K, used 2906K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 27% used [0x00000000ff600000,0x00000000ff83ca88,0x00000000ffe00000)
from space 1024K, 60% used [0x00000000ffe00000,0x00000000ffe9a020,0x00000000fff00000)
to space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
ParOldGen total 20480K, used 4616K [0x00000000fe200000, 0x00000000ff600000, 0x00000000ff600000)
object space 20480K, 22% used [0x00000000fe200000,0x00000000fe682030,0x00000000ff600000)
Metaspace used 2784K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 300K, capacity 386K, committed 512K, reserved 1048576K
判断一个对象是否存活,除了GC Roots引用之外,还有一个条件就是对象是否重写了finalize方法,如果对象重写了该方法,则会交给FQueue队列去执行,如果执行该方法后被重新关联,则在下次回收时不会被回收,否则下次回收,该方法只执行一次。
测试代码:
private static final int _1M = 1024 * 1024;
private static void testMinorGCOverriddeFinalize() {
/* Eden区为8M,from/to space各为1M */
LargeObjectOverrideFinalize largeOb1 = new LargeObjectOverrideFinalize(_1M * 1 / 2, "largeOb1");
LargeObjectOverrideFinalize largeOb2 = new LargeObjectOverrideFinalize(_1M * 1, "largeOb2");
LargeObjectOverrideFinalize largeOb3 = new LargeObjectOverrideFinalize(_1M * 2, "largeOb3");
largeOb1 = null;
largeOb3 = null;
LargeObjectOverrideFinalize largeOb4 = new LargeObjectOverrideFinalize(_1M * 3, "largeOb4");
LargeObjectOverrideFinalize largeOb5 = new LargeObjectOverrideFinalize(_1M * 2, "largeOb5");
/* 保证调用finalize完毕 */
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.gc();
}
public static void main(String[] agrs) {
testMinorGCOverriddeFinalize();
}
static class LargeObjectOverrideFinalize {
private byte[] data;
private String name;
public LargeObjectOverrideFinalize(int size, String name) {
data = new byte[size];
this.name = name;
System.out.println("Over Constructing LargeObjectOverrideFinalize " + name + System.lineSeparator());
}
public String getName() {
return name;
}
@Override
public void finalize() {
System.out.println(
"F