年轻代GC过程

当需要在堆中创建一个新的对象,而年轻代内存不足时触发一次GC,在年轻代触发的GC称为普通GC,Minor GC。注意到年轻代中的对象都是存活时间较短的对象,所以适合使用复制算法。这里肯定不会使用两倍的内存来实现复制算法了,牛人们是这样解决的,把年轻代内存组成是80%的Eden、10%的From Space和10%的To Space,然后在这些内存区域直接进行复制。

刚开始创建的对象是在Eden中,此时Eden中有对象,而两个survivor区没有对象,都是空闲区间。第一次Minor GC后,存活的对象被放到其中一个survivor,Eden中的内存空间直接被回收。在下一次GC到来时,Eden和一个survivor中又创建满了对象,这个时候GC清除的就是Eden和这个放满对象的survivor组成的大区域(占90%),Minor GC使用复制算法把活的对象复制到另一个空闲的survivor区间,然后直接回收之前90%的内存。周而复始。始终会有一个10%空闲的survivor区间,作为下一次Minor GC存放对象的准备空间。

要完成上面的算法,每次Minor GC过程都要满足:

存活的对象大小都不能超过survivor那10%的内存空间,不然就没有空间复制剩下的对象了。但是,万一超过了呢?前面我们提到过年老代,对,就是把这些大对象放到年老代。

年老代GC

什么样的对象可以进入年老代呢?如下:

在年轻代中,如果一个对象的年龄(GC一次后还存活的对象年岁加1)达到一个阈值(可以配置),就会被移动到年老代。

Survivor中相同年龄的对象大小总和超过survivor空间的一半,则不小于这个年龄的对象都会直接进入年老代。

创建的对象的大小超过设定阈值,这个对象会被直接存进年老代。

年轻代中大于survivor空间的对象,Minor GC时会被移进年老代。

年老代中的对象特点就是存活时间较长,而且没有备用的空闲空间,所以显然不适合使用复制算法了,这个时候使用标记-清除算法或者标记-整理算法来实现GC。负责年老代中GC操作的是全局GC,Major GC,Full GC。

什么时候触发Major GC呢?

在Minor GC时,先检测JVM的统计数据,查看历史上进入老年代的对象平均大小是否大于目前年老代中的剩余空间,如果大于则触发Full GC。

Sun/OracleJDK GC组合方式

收集器模式 新生代GC方式 老年代和持久代GC方式
-XX:+UseSerialGC Serial 串行GC Serial Old 串行GC
-XX:+UseParallelGC Parallel Scavenge 并行回收GC Serial Old 并行GC
-XX:+UseConcMarkSweepGC ParNew 并行GC CMS 并发GC 当出现“Concurrent Mode Failure”时 采用Serial Old 串行GC
-XX:+UseParNewGC ParNew 并行GC Serial Old 串行GC
-XX:+UseParallelOldGC Parallel Scavenge 并行回收GC Parallel Old 并行GC
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC Serial 串行GC CMS 并发GC 当出现“Concurrent Mode Failure”时 采用Serial Old 串行GC