垃圾回收算法,垃圾回收机制
1. 垃圾回收算法
1. 标记清除
- 标记-清除算法将垃圾回收分为两个阶段:标记阶段和清除阶段。
- 在标记阶段,首先通过根节点(GC Roots),标记所有从根节点开始的对象,
- 未被标记的对象就是未被引用 的垃圾对象。
- 然后清除阶段,清除所有未被标记的对象。
- 适用场合:
- 存活对象较多的情况下比较高效
- 适用于年老代
- 缺点
- 容易产生内存碎片,再来一个比较大的对象时(典型情况:该对象的大小大于空闲表中的每一块大小,但是小于其中两块的和),会提前触发垃圾回收。
- 扫描了整个空间两次(第一次:标记存活对象;第二次:清除没有标记的对象)。
2. 复制算法
- 基本思想
- 从根 集合节点进行扫描,标记出所有的存活对象,
- 并将这些存活的对象复制到一块新的内存上。
- 之后原来的那一块儿内存全部回收掉。
现在的商业虚拟机都采用这种收集算法来回收新生代。
- 使用场合
- 存活对象较少的情况下比较高效
- 扫描了整个空间一次(标记存活对象并复制移动)
- 适用于年轻代(即新生代):基本上98%的对象朝生夕死,存活下来的会很少
- 缺点
- 需要一块儿空的内存空间
- 需要复制移动对象
- 复制算法的高效,建立在存活对象少、垃圾对象多的前提下。
- 这种情况在新生代经常发生, * 但是在老年代更常见的情况是大部分对象都是存活对象。 如果依然使用复制算法,由于存活的对象较多,复制的成本也很高。
3. 标记整理
标记-压缩算法是一种老年代的回收算法,它在标记-清除算法的基础上做了些优化
- 首先,从根节点开始对所有可达对象做一次标记
- 之后将所有的存活对象 压缩到内存的一端。
- 清理边界外所有的空间。
这样既避免了碎片的产生,又不需要两块相同的内存空间。
4. 分代收集算法
分代手机算法就是目前虚拟机使用的回收算法。
- 它解决了标记整理不适合用于老年代的问题,
- 将内存分为 各个年代。
- 一般情况下将堆区划分为老年代(Tenured Generation)和新生代(Young Generation)
- 在堆区之外还有一个代就是永久代(Permanet Generation)。
- 在不同年代使用不同的算法,从而使用最合适的算法,
- 新生代存活率低,可以使用复制算法。
- 而老年代对象存活率高,没有额外空间对它进行分配担保,所以只能使用标记-清除或者标记-整理算法。
2. 垃圾回收机制
年轻代
年轻代分为Eden区和Survivor区(两块儿:from和to)且Eden: from:to = 8:1:1
新产生的对象
新产生的对象优先分配在Eden区(除非配置了 -XX:PretenureSizeThreshold, 大于该值的对象会直接进入老年代)
当Eden区满了或放不下了
这时候其中存活的对象会复制到from区。
- 这里需要注意的是,如果存活下来的对象from区都放不下
- 则这些存活下来的对象全部进入 年老代。
- 之后Eden区的内存全部回收掉
之后产生的对象会继续分配在Eden区。
- 当Eden区又满了或放不下了,这时候会把Eden区和from区存活下来的对象复制到to区
- (同理,如果存活下来的对象to区都放不下,则这些存活下来的对象,全部进入年老代)
- 之后回收掉Eden区和from区的所有内存。
如上,会有很多对象会被复制很多次(每复制一次,对象的年龄+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. 年轻代:复制算法
-
所有新生成的对象首先都是放在年轻代的,年轻代的目标就是尽可能快速的收集掉 那些生命周期短的对象。
-
新生代内存按照8:1:1的比例分为一个Eden区,和两个survivor(to,from)区。
- 大部分对象在Eden区中生成。
- 回收时先将Eden区存活对象复制到一个from区,然后清空Eden区。
- 当from区也存放满了时,则将Eden区和from区存活对象复制到另一个to区,
- 然后晴空Eden和From区。
- 此时From区是空的。
- 然后将From和to区交换,即保持to区为空,如此往复。
- 当to区不足以存放Eden和From的存活对象时
- 就将存活对象直接放到老年代
- 若是年老代也满了,就触发一次Full GC(Major GC),也就是新生代、老年代都回收。
- 新生代发生的GC,叫做Minor GC,Minor GC发生频率比较高(不一定等Eden区满了才触发)
2. 年老代:标记-清除或标记-整理
- 年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。
- 因此,可以认为年老代中存放的都是一些生命周期较长的对象
- 内存比新生代也大很多(大概比例是1:2)。
- 当年老代内存满时触发Major GC即Full GC
- Full GC频率比较低,老年代对象存活时间比较长,存活率标记高。
以上年轻代与年老代分别采用不同回收算法的方式 称为分代收集算法。

浙公网安备 33010602011771号