1 年轻代只设置一个Eden区行不行?
如果只设置一个Eden区,那么没进行一次MinorGC,存活的对象就会被送入老年代,老年代很快被填满,就会触发MajorGC,因为MajorGC之前会先进行一次MinorGC,所以也可以看做是发生了Full GC,Full GC消耗的时间要远远大于Minor GC,这样会增加系统停顿时间。那么,你们也许会想,可以增加老年代的空间,减少Full GC的频率,可以是频率虽然降低了,但是由于老年代存储的对象太多,一旦发生Full GC,单次GC的时间增加了,系统停顿时间依然很长。反之,单次GC时间减少,但是GC频率增加了,结果还是一样的。
所以,我们减少Full GC的方案只有一个:减少往老年代发送的对象,进而更慢地触发Full GC。所以,我们才需要Survivor区来进行筛选,只要经历了16次Minor GC还能在年轻代存活的对象才有资格送往老年代。
2 设置一个Eden区和一个Survivor区行不行?
我们已经知道了必须需要设置Survivor区,如果只要一个Survivor区,MinorGC流程是:
- 把new对象放在Eden中,一旦Eden满了,触发一次Minor GC,Eden中的存活对象就会移动到Survivor区,同时Eden区被清空。
- 然后继续new对象放在Eden区中,一旦Eden满了,又触发一个Minor GC,此时,Eden和Survivor中各有一些存活的对象,尤其是Survivor区发生了GC后,剩余存活的对象肯定不是紧密排列的,如果此时把Eden区中存活对象强行移动到Survivor区中,由于这Survivor区域的对象不是连续的,所以会产生内存碎片。
所以,我们建立两个Survivor区看看如何。
3 建立两个Survivor区怎么样?
建立两个Survivor区,分别是S0和S1,MinorGC流程如下:
- 把new的对象放在Eden区中,一旦Eden满了,触发一次Minor GC,Eden区中存活的对象会送到S0中,Eden被清空;
- 再次new对象放入Eden区中,Eden又满了,触发Minor GC,Eden和S0中存活的对象放入到S1中,然后S0和Eden清空,然后S0和S1交换角色,S1变成了S0,作为下一次GC的主要目标,S0变成了S1,存放下一次GC存活的对象。
- 如此循环往复,对象头的分代年龄达到16次,则会被送到老年代中。
有两个Survivor的好处就是:永远有一个S区是空的,另外一个S区无碎片。如果Survivor细分为更多块,每一块的空间会比较小,很容易导致Survivor满,对象的分代年龄增加的越快,导致送往老年代的对象越多,所以两个S区是经过权衡后的最佳方案。