垃圾回收算法,垃圾回收机制

1. 垃圾回收算法

1. 标记清除

  1. 标记-清除算法将垃圾回收分为两个阶段:标记阶段和清除阶段。
  • 在标记阶段,首先通过根节点(GC Roots),标记所有从根节点开始的对象,
  • 未被标记的对象就是未被引用 的垃圾对象。
  • 然后清除阶段,清除所有未被标记的对象。
  1. 适用场合:
  • 存活对象较多的情况下比较高效
  • 适用于年老代
  1. 缺点
  • 容易产生内存碎片,再来一个比较大的对象时(典型情况:该对象的大小大于空闲表中的每一块大小,但是小于其中两块的和),会提前触发垃圾回收。
  • 扫描了整个空间两次(第一次:标记存活对象;第二次:清除没有标记的对象)。

2. 复制算法

  1. 基本思想
  • 从根 集合节点进行扫描,标记出所有的存活对象,
  • 并将这些存活的对象复制到一块新的内存上。
  • 之后原来的那一块儿内存全部回收掉。

现在的商业虚拟机都采用这种收集算法来回收新生代。

  1. 使用场合
  • 存活对象较少的情况下比较高效
  • 扫描了整个空间一次(标记存活对象并复制移动)
  • 适用于年轻代(即新生代):基本上98%的对象朝生夕死,存活下来的会很少
  1. 缺点
  • 需要一块儿空的内存空间
  • 需要复制移动对象
  • 复制算法的高效,建立在存活对象少、垃圾对象多的前提下。
    • 这种情况在新生代经常发生, * 但是在老年代更常见的情况是大部分对象都是存活对象。 如果依然使用复制算法,由于存活的对象较多,复制的成本也很高。

3. 标记整理

标记-压缩算法是一种老年代的回收算法,它在标记-清除算法的基础上做了些优化

  • 首先,从根节点开始对所有可达对象做一次标记
  • 之后将所有的存活对象 压缩到内存的一端。
  • 清理边界外所有的空间。

这样既避免了碎片的产生,又不需要两块相同的内存空间。

4. 分代收集算法

分代手机算法就是目前虚拟机使用的回收算法。

  1. 它解决了标记整理不适合用于老年代的问题,
  • 将内存分为 各个年代。
  • 一般情况下将堆区划分为老年代(Tenured Generation)和新生代(Young Generation)
  • 在堆区之外还有一个代就是永久代(Permanet Generation)。
  1. 在不同年代使用不同的算法,从而使用最合适的算法,
  • 新生代存活率低,可以使用复制算法。
  • 而老年代对象存活率高,没有额外空间对它进行分配担保,所以只能使用标记-清除或者标记-整理算法。

2. 垃圾回收机制

年轻代

年轻代分为Eden区和Survivor区(两块儿:from和to)且Eden: from:to = 8:1:1

新产生的对象

新产生的对象优先分配在Eden区(除非配置了 -XX:PretenureSizeThreshold, 大于该值的对象会直接进入老年代)

当Eden区满了或放不下了

这时候其中存活的对象会复制到from区。

  • 这里需要注意的是,如果存活下来的对象from区都放不下
  • 则这些存活下来的对象全部进入 年老代。
  • 之后Eden区的内存全部回收掉

之后产生的对象会继续分配在Eden区。

  1. 当Eden区又满了或放不下了,这时候会把Eden区和from区存活下来的对象复制到to区
  2. (同理,如果存活下来的对象to区都放不下,则这些存活下来的对象,全部进入年老代)
  3. 之后回收掉Eden区和from区的所有内存。

如上,会有很多对象会被复制很多次(每复制一次,对象的年龄+1)

  1. 默认情况下,当对象被复制了15次(这个次数可以通过:-XX:MaxTenuringThreshold来配置),就会进入年老代了。

当年老代满了

或者存放不下将要进入年老代的存活对象的时候,就会发生一次Full GC(这是我们 需要减少的,因为耗时严重)

垃圾回收的两种类型

1. Minor GC

  • 对新生代进行回收,不会影响到年老代。
  • 因为新生代的Java对象大多死亡频繁,所以Minor GC非常频繁。
  • 一般在这里使用速度快,效率高的算法,使垃圾回收能尽快完成。

2. Full GC

  • 也叫Major GC, 对整个堆进行回收。
  • 包括新生代和老年代
  • 因为Full GC需要对整个堆进行回收,所以比Minor GC要慢,因此应该尽 可能减少 Full GC的次数
  • 导致Full GC的原因包括:
    • 老年代被写满
    • 永久代(Perm)被写满
    • System.gc()被显式调用等。

4. 总结

1. 年轻代:复制算法

  1. 所有新生成的对象首先都是放在年轻代的,年轻代的目标就是尽可能快速的收集掉 那些生命周期短的对象。

  2. 新生代内存按照8:1:1的比例分为一个Eden区,和两个survivor(to,from)区。

  • 大部分对象在Eden区中生成。
  • 回收时先将Eden区存活对象复制到一个from区,然后清空Eden区。
  • 当from区也存放满了时,则将Eden区和from区存活对象复制到另一个to区,
    • 然后晴空Eden和From区。
    • 此时From区是空的。
    • 然后将From和to区交换,即保持to区为空,如此往复。
  1. 当to区不足以存放Eden和From的存活对象时
  • 就将存活对象直接放到老年代
  • 若是年老代也满了,就触发一次Full GC(Major GC),也就是新生代、老年代都回收。
  1. 新生代发生的GC,叫做Minor GC,Minor GC发生频率比较高(不一定等Eden区满了才触发)

2. 年老代:标记-清除或标记-整理

  1. 年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。
  • 因此,可以认为年老代中存放的都是一些生命周期较长的对象
  1. 内存比新生代也大很多(大概比例是1:2)。
  • 当年老代内存满时触发Major GC即Full GC
  • Full GC频率比较低,老年代对象存活时间比较长,存活率标记高。

以上年轻代与年老代分别采用不同回收算法的方式 称为分代收集算法。

posted @ 2025-05-18 20:10  kuki'  阅读(34)  评论(0)    收藏  举报