分代收集理论
分代收集理论建立在下面两个假说之上:
-
弱分代假说(Weak Generational Hypothesis):绝大多数对象都是朝生夕死。
-
强分带假说(Strong Generational Hypothesis):熬过越多次垃圾收集过程的对象就越难以消亡。
根据这两条假说设计的垃圾收集原则:收集器应该将Java堆划分出不同的区域,然后将回收对象依据其年龄分配到不同的区域中存储。设计者一般至少把Java堆划分为新生代(Young Generation)和老年代(Old Generation)两个区域。
这种设计也有一个明显的困难,跨代引用问题。为了解决这个问题,就需要对分代收集理论添加了第三条经验法则:跨代引用假说(Intergenerational Reference Hypothesis),跨代引用相对于同代引用来说仅占极少数。根据这个假说只需在新生代上建立一个全局的数据结构(该结构被称为“记忆集”,Remembered Set),这个数据结构把老年代的哪一块内存会存在跨代引用,当发生Minor GC时,只用包含跨代引用的小块内存里的对象会被加入到GC Roots进行扫描。
标记-清除算法
首先标记出所有要回收的对象,在标记完成之后,统一回收掉所有被标记的对象,也可以反过来,标记活着的对象,统一回收所有未被标记的对象。
两个缺点:
- 执行效率不稳定,标记和清除两个过程的执行效率随对象数量的增长而降低。
- 内存空间碎片化问题
标记-复制算法
将内存大小容量划分为大小相等的两块,每次只是用其中一块,当这一块内存用完的时候就将还存活的对象复制到另外一块上面,让后把已使用的内存清理掉。这种算法的缺点就是内存缩小了一半,空间浪费太多。
Java虚拟机使用这种算法回收新生代,因为新生代中大多数都熬不过第一轮收集,将内存分为一个较大的Eden空间和两小块较小的Survivor空间,每次分配内存只使用Eden和其中一个Survivor。当发生垃圾收集的时候,将Eden和Survivor中仍然存活的对象一次性复制到另外的一个Survivor空间上,如果另外一个Survivor空间不足,将依赖其他内存区域(实际上大多数是老年代)进行分配担保(Handle Promotion),然后直接清理掉Eden和已使用过的Survivor。HotSpot虚拟机默认Eden和Survivor的空间比为8:1。
标记-整理算法
其过程和“标记-清除”算法一样,但后续步骤不直接对可回收对象进行清理,而是让或者的对象向内存空间的一端移动,然后直接清除掉边界以外的内存。
移动活的对象,尤其是老年代这种每次回收还有大量对象存活区域,移动存活对象并更新所有引用这些对象的地方将会是一种非常负重的操作,而且这种操作会暂停用户应用进程才能进行。移动导致内存回收会更复杂,不移动则内存分配会更复杂。
一种这种的方案,平时让虚拟机都采用“标记-清除”算法,暂时容忍碎片的存在,直到内存空间碎片化程度已经达到影响内存分配时,采用“标记-整理”算法收集一次,已获得规整的内存空间。