垃圾回收算法

垃圾收集算法

  1. 标记 - 清除

    将存活的对象标记, 然后清理掉未标记的对象.

    缺点:

    1. 标记和清除的效率不高.

    2. 产生大量内存碎片, 不能给大的对象分配内存.

  2. 标记 - 整理

    将标记的对象放在连续的空间, 清除掉空间以外的内存.

  3. 复制

    将内存分成两块, 每次只用其中的一半, 当这一半用完了, 就将存活的对象放到另一半, 然后把当前这一半回收.

    Java中的新生代就是用的这个方法, 但是是将内存分成了较大的Eden区和两个Survivor区, 每次使用Eden和一个Survivor. 回收的时候, 将存活的对象放在另一个Survivor区. Eden占80, Survivorge占10%. 如果回收时候有超过一个Survivor大小的对象, 需要老年代进行分配担保, 也就是借用老年代的空间暂存对象.

  4. 分代收集

    Java堆分成新生代和老年代. 新生代采用复制算法, 老年代采用标记 - 清除或者标记 - 整理的方法.

Minor GC 和 Full GC

  1. Minor GC: 发生在新生代上, 因为新生代的对象存活时间短, 因此会频繁执行, 执行速度也比较快.

  2. Full GC: 发生在老年代, 老年代对象存活时间长, 很少执行, 执行速度也比较慢.

内存分配策略

  1. 优先分配在Eden

    大多数情况下, 对象在新生代Eden区分配, 如果空间不够, 发起一次Minor GC.

  2. 大对象直接进入老年代

    大对象是指需要连续分配很多空间的, 比如数组.

    经常出现大对象会提前触发GC来获取足够的连续空间存放.

    -XX:PretenureSizeThreshold, 大于此值的对象直接在老年代分配.

  3. 长期存活的对象进入老年代

    为对象定义年龄计数器, 对象在Eden出生并且经过Minor GC后存活, 将移动到Survivor, 年龄加一, 增加到一定年龄进入老年代.

    -XX:MaxTenuringThreshold, 用来定义年龄阈值.

  4. 动态对象年龄判定

    如果Survivor中的所有相同年龄对象的总和大于一半, 那么大于等于这个年龄的对象都进入老年代.

  5. 空间分配担保

    在Minor GC之前, 会检查老年代中最大的连续空间是否大于新生代所有对象的总和大小, 如果可以, 那么进行Minor GC. 否则看HandlePromotionFailure是否允许担保失败, 如果允许那么检查最大连续空间是否大于历次进入老年代对象总和的平均大小, 如果满足则尝试进行Minor GC, 否则进行Full GC.

触发Full GC条件

只要Eden满, 就会出发Minor GC, 而Full GC出发有多种条件:

  1. 调用System.gc()

    这个方法只是建议JVM执行一次Full GC, JVM不一定真的执行.

  2. 老年代空间不足

  3. 空间担保失败