一、对象在Eden区分配
验证:对象优先在Eden区分配,当Eden区没有足够空间时,会引发一次Minor GC
VM Options:-verbose:gc -Xms20M -Xmx20M -Xmn5M -XX: +PrintGCDetails -XX: SurvivorRatio=8
参数说明:
-verbose:gc 在控制台输出GC情况
-XX: +PrintGCDetails 在垃圾回收时打印详细内存日志
-Xms10M 最小堆内存20M
-Xmx10M 最大堆内存20M
-Xmn5M 年轻代5M
-XX: SurvivorRatio=8 Eden与Survivor比例为8:1:1
运行代码:

运行结果:

从运行结果可以看出,allocation3在分配空间时引发了一次Minor GC,从GC日志当中可以看出 :
(1)GC 产生的原因为Allocation Failure,即年轻代中没有足够的空间能够存储新的数据;
(2)垃圾回收器采用的是Parallel Scavenge收集器,PSYoungGen表示新生代,这个名称由垃圾回收器决定的。
(3)[PSYoungGen: 3809K->488K(4608K)]表示:GC前新生代内存区域已使用3809K,GC后该内存区域已使用488K,内存区域总容量为4608K。
(4)5857K->4828K(19968K), 0.0009839 secs表示:GC前Java堆已使用5857K,GC后Java堆已使用4828K,Java堆总容量为19968K,本次新生代回收时间0.0009839 secs。
(5)PSYoungGen total 4608K, used 2702K 表示:新生代总空间4608K,已使用2702K ,其中Eden空间4096K,已使用54%,From Survivor空间总共512K,已使用95%,To Survivor空间共计512K,已使用0%;
(6)ParOldGen total 15360K, used 4340K表示:老年代共计15360K,已使用4340K;
(7)Metaspace used 3267K, capacity 4496K, committed 4864K, reserved 1056768K 表示:元空间已加载的类的空间量3267K,分配块的元数据空间大小4496K,空间块大小4864K,元数据的空间保留量为1056768K 。
分析:
由GC日志结果可以看出,allocation1和allocation2共计占用了Eden空间的4096K,当allocation3开始创建时,发现Eden没有足够的存储空间而引发了一次Minor GC,垃圾收集器在进行垃圾回收时,发现已有的2个2M大小的对象,无法全部放入到Survivor空间,所以只好放入老年代中,老年代已使用的4M正对应allocation1和allocation2,GC后1M的allocation3和1M的allocation4顺利的分配到了Eden空间上,占据一半的Eden空间。
二、大对象直接进入老年代
验证:大对象直接进入老年代
VM Options:-verbose:gc -Xms20M -Xmx20M -Xmn5M -XX: +PrintGCDetails -XX: SurvivorRatio=8
参数说明:
-verbose:gc 在控制台输出GC情况
-XX: +PrintGCDetails 在垃圾回收时打印详细内存日志
-Xms10M 最小堆内存20M
-Xmx10M 最大堆内存20M
-Xmn5M 年轻代5M
-XX: SurvivorRatio=8 Eden与Survivor比例为8:1:1
运行代码:

运行结果:

分析:
由运行结果日志可以看出,局部变量_1MB在新生代上分配,当8M的大对象allocation1进入,因在新生代无法找到足够的内存空间,被直接分配在了老年代,ParOldGen used 8192K 正好是allocation1使用的8M。
三、长期存活的对象进入老年代
验证:长期存活的对象进入老年代
VM Options:-verbose:gc -Xms20M -Xmx20M -Xmn5M -XX:MaxTenuringThreshold=1 -XX: +PrintGCDetails -XX: SurvivorRatio=8
参数说明:
-verbose:gc 在控制台输出GC情况
-XX: +PrintGCDetails 在垃圾回收时打印详细内存日志
-Xms10M 最小堆内存20M
-Xmx10M 最大堆内存20M
-Xmn5M 年轻代5M
-XX: SurvivorRatio=8 Eden与Survivor比例为8:1:1
-XX:MaxTenuringThreshold=1 对象晋升老年代的年龄阈值为1
运行代码:

运行结果:


运行结果:

分析:
从前面章节了解到对象会先进入Eden空间,从图 3-2 运行结果可以看出allocation1在新生代分配,allocation2大对象直接进入老年代;
图 3-4 运行结果看出allocation3创建时引发了一次Minor GC,因-XX:MaxTenuringThreshold=1的缘故,经历了一次GC,allocation1的分代年龄加一,晋升到老年代,与allocation2共占据5M空间。
四、 动态对象年龄判定
HotSpot虚拟机并不是必须要对象的年龄达到XX:MaxTenuringThreshold 指定的阈值才能晋升老年代,如果Survivor空间中,相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无需等到XX:MaxTenuringThreshold 指定的年龄。
VM Options:-verbose:gc -Xms20M -Xmx20M -Xmn5M -XX:MaxTenuringThreshold=2 -XX: +PrintGCDetails -XX: SurvivorRatio=8
参数说明:
-verbose:gc 在控制台输出GC情况
-XX: +PrintGCDetails 在垃圾回收时打印详细内存日志
-Xms10M 最小堆内存20M
-Xmx10M 最大堆内存20M
-Xmn4M 年轻代4M
-XX: SurvivorRatio=8 Eden与Survivor比例为8:1:1
-XX:MaxTenuringThreshold=2 对象晋升老年代的年龄阈值为2
运行代码:

运行结果:

分析:
只经历了一次GC,allocation1和allocation2的GC分代年龄为1,没有达到XX:MaxTenuringThreshold 指定的年龄。而allocation1和allocation2占用的空间大于Survivor空间的一半,直接进入老年代。
五、空间分配担保
在Minor GC发生前,Java虚拟机必须检查老年代最大可用的连续空间是否大于新生代所有对象的总空间,如果成立,则本次Minor GC是安全的;否则,虚拟机会查看-XX:HandlerPromotionFailure参数的设置值是否允许担保失败:如果允许担保失败,虚拟机将会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将进行一次Minor GC;如果小于,或者 -XX:HandlerPromotionFailure 为false,则不会进行Minor GC,改为进行一次Full GC。
新生代采用复制算法,其中一个Survivor空间始终作为轮换备份空间,假设最极端的情况,GC后,新生代的所有对象依然存活,那么就需要老年代进行分配担保,把Survivor无法容纳的对象送入老年代。老年代担保的前提是本身还有空间能容纳这些对象,但老年代不知道本次回收后会有多少存活对象,因此只能对之前每一次晋升到老年代的对象取平均值,并与老年代剩余空间做比较,从而决定是否触发Full GC来腾出更多的空间。
这种担保的做法存在风险,假如本次Minor GC后存活的对象突增,依然会存在担保失败的情况,而不得不造成更长停顿时间的Full GC。