偶然想起JVM年轻代中Eden区和两个Survivor的比例,就想根据自己的理解和大家探讨一下为什么要配成8 :1 :1的比例。
一、JVM堆分代
1、JVM堆被分为了年轻代和老年代。年轻代的GC过程称为Yong GC,速度快较频繁。老年代的GC过程称为Full GC,速度较慢应该尽量避免。
2、对象被创建后,除了少部分大对象会在老年代分配内存外,大部分的对象首先都是在年轻代进行内存分配,而且大部分的对象都是“朝生夕死”,很快就会被年轻代的Yong GC回收掉。
3、老年代的内存空间一般会比年轻代的内存空间大,能存放的对象多,老年代的空间不足后会进行Full GC操作,比Yong GC耗时,所以应尽量避免频繁的Full GC操作。
二、年轻代的分区
1、年轻代中分为一个Eden区和两个Surviver区,比例为8:1:1,两个Surviver区分别称为“From”区和“To”区。对象在Eden区创建,经过一次Yong GC后,还存活的对象将会被复制到Surviver区的“From”区,此时“To”区是空的。到了下一次GC的时候,Eden区还存活的对象会复制到Surviver区的“To”区,而“Form”区的对象有两个去处,“From”区的对象会根据经过的GC次数计算年龄,如果年龄到达了阈值(默认15),则会被移动到老年代中,否则就复制到“To”区,此时“From”区变成了空的,然后“From”区和“To”区进行角色互换,到下一次进行GC时,还是有一块空的“To”区,用来存放从eden区和“From”区移动过来的对象。
2、那这种分区有什么好处呢?
a、在年轻代新增Surviver区,有利于减轻老年代的负担,尽可能的让大部分对象在年轻代通过较高效的Yong GC回收掉,不至于老年代里存放的对象过多导致内存不足而进行频繁的Full GC操作。
b、这种分区有利于减少内存碎片的产生。
首先我们来看看,如果年轻代只分为Eden区和Surviver区两个区域并且比例是8:2的时候,内存的回收和分配情况会怎么样。第一次Yong GC后,Eden区还存活的对象移动到Surviver区,Surviver区还存活的对象保留在Surviver区,而这些对象的内存是不连续的,Surviver区里就会产生很多内存碎片,这就会导致有些大对象要移动到Surviver区的时候,没有足够的连续内存进行分配,而不得不移动到老年代中,增加老年代的负担,降低效率。
然后我们看看Eden区和Surviver区的比例是8:1:1时会有什么样的效果。第一次Yong GC后,Eden区还存活的对象复制到Surviver区的“To”区,“From”区还存活的对象也复制到“To”区,再清空Eden区和From区,这样就等于“From”区完全是空的了,而“To”区也不会有内存碎片产生,等到第二次Yong GC时,“From”区和“To”区角色互换,很好的解决了内存碎片的问题。
如下图所示:
有人要说为什么一定是8:1:1的比例呢,这大概是前辈们实践出来的最佳比例吧~~