为了解决效率问题,“复制”收集算法出现了。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块内存用完,需要进行垃圾收集时,就将存活者的对象复制到另一块上面,然后将第一块内存全部清除。这种算法有优有劣:

  • 优点:不会有内存碎片的问题。
  • 缺点:内存缩小为原来的一半,浪费空间。

为了解决空间利用率问题,可以将内存中的新生代分为三块: Eden、From Survivor、To Survivor,比例是 8:1:1,每次使用 Eden 和其中一块 Survivor。回收时,将 Eden 和 Survivor 中还存活的对象一次性复制到另外一块 Survivor 空间上,最后清理掉 Eden 和刚才使用的 Survivor 空间。这样只有 10% 的内存被浪费。

但是我们无法保证每次回收都只有不多于 10% 的对象存活,当 Survivor 空间不够,需要依赖其他内存(指老年代)进行分配担保。

分配担保

为对象分配内存空间时,如果 Eden+Survivor 中空闲区域无法装下该对象,会触发 MinorGC 进行垃圾收集。但如果 Minor GC 过后依然有超过 10% 的对象存活,这样存活的对象直接通过分配担保机制进入老年代,然后再将新对象存入 Eden 区。

优点

  • 没有标记和清除的过程,实现简单高效
  • 复制过去以后的保证空间的连续性,不会出现碎片的问题

缺点

  • 需要2倍空间
  • 对于G1这种拆分为大量region的GC,复制而不是移动,意味着GC需要维护region之间的引用关系,不管是内存占用或者时间开销也不小
  • +如果系统中的垃圾对象很多,需要复制的存活对象数量并不会太大,或者非常低才行