1. Young GC 什么时候触发
Young GC 一般是在新生代的 Eden 区满了之后触发的,之后采用复制算法在 Survivor 的 from 和 to 区之间来回收新生代的垃圾对象。
2. Old GC 和 Full GC什么时候触发
- 在每次发生 Young GC 之前会进行检查,当老年代可用内存小于新生代全部对象的大小,而这时候没开启空间担保参数(HandlePromotionFailure=false),会直接触发Full GC;当开启空间担保参数(HandlePromotionFailure=true)时,如果【老年代可用的连续内存空间】 < 【新生代历次 Young GC 后升入老年代的对象总和的平均大小】,则说明本次 Young GC 后升入老年代的对象大小可能会超过老年代当前可用内存空间。此时必须先触发一次 Old GC 让老年代腾出更多的空间,而后再执行 Young GC。
- 那么执行 Young GC 之后有一批对象需要放入老年代,此时老年代就是没有足够的内存空间存放这些对象了,此时必须立即触发一次 Old GC
- 如果老年代内存使用率超过了 92%,也要直接触发 Old GC,当然这个比例是可以通过参数调整的(Java8中,-XX:CMSInitiatingOccupancyFraction的默认值是92)
其实满足上述一些条件的时候,在 GC 日志中就能看到 Full GC 的字样,同时在 Full GC 中会看到同时对年轻代、老年代、永久代三个区域都进行了垃圾回收。
那么触发 Full GC 的时候,是先执行 Young GC 还是先执行 Old GC?
因为不同的Full GC触发条件其实是不一样的,而且不同的JVM版本的实现机制也不同。所以也没办法给一个准确的定义。
所以我们可以这样说,上述条件满足时触发 Full GC,Full GC一般会带上一次 Young GC 去回收新生代,同时也会有 Old GC 也回收老年代,还会去回收永久代。
3. 永久代满了之后会怎么样?
Full GC 有上述几个触发条件,同时触发 Full GC 的时候其实会带上针对新生代的 Young GC,也会有针对老年代的 Full GC,还会有针对永久代的GC。所以假如存放类信息、常量池的永久代满了之后,就会触发一次Full GC。
这样Full GC执行的时候,就会顺带把永久代中的垃圾给回收了,但是永久代中的垃圾一般是很少的,因为里面存放的都是一些类,还有常量池之类的东西,这些东西通常来说是不需要回收的。如果永久代真的放满了,回收之后发现没腾出来更多的地方,此时只能抛出OOM的异常了。
参考: