垃圾收集算法
垃圾收集算法可分为“引用计数式垃圾收集”和“追踪式垃圾收集”
分带收集理论
分代收集名为理论,实质是一套复合大多数程序运行实际情况的经验法则,它建立在两个分代假说之上:
(1)弱分代假说:绝大多数对象都是朝生夕灭的
(2)强分代假说:熬过越多次垃圾回收过程的对象就越难以消亡。
(3)跨带引用假说:跨代引用相对于同代引用来说仅极其少数
这两个分代假说共同奠定了多款常用的垃圾收集齐的一致的设计原则:收集器应该将Java堆划分出不同的区域,然后将回收对象依据其年龄(年龄即熬过垃圾回收集过程的次数)分配到不同的区域之中存储。
为了防止少量跨代引用扫描整个老年代,在新生代上建立一个全局数据结构(记忆集)这个结构把老年代划分为若干小块,标识出老年代的哪一块内存会存在跨代引用。当发生MinorGC时,只有包含了跨代引用的小块内存才会被加入到 GCRoots进行扫描
一些名词:
- 部分收集(Partial GC):指目标不是完整收集整个Java堆的垃圾收集,其中分为:
- 新生代手机(Minor GC/Young GC):指目标只是新生代的垃圾收集
- 老年代收集(Major GC/Old GC):指目标只是老年代的垃圾收集。目前只有CMS收集器会有单独收集老年代的行为。
- 混合收集(Mixed GC):指目标是手机整个新生代以及部分老年代的垃圾收集
- 整堆收集(Full GC):收集整个Java堆和方法区的垃圾收集
标记-清除算法
分别标记清除两个阶段:
- 首先标记出所有需要回收的对象,在标记完成后,同一回收掉所有被标记的对象。
- 或
- 标记存活的对象,统一回收所有未被标记的都西昂
缺点:
- 执行效率不稳定,如果Java堆中包含大量对象,而其中大部分是需要被回收的,这时必须进行大量标记和清除动作,导致标记和清除两个过程的执行效率都随对象数量增长而降低
- 内存空间碎片化问题,标记、清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致当以后再程序运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。
标记-复制算法
标记复制算法常被简称为复制算法,为了解决标记-清除算法面对大量可回收对象时执行效率低的问题,提出了一种“半区复制”的垃圾收集算法,它将可用内存按容量划分为大小相等的两块,每次只利用其中的一块。当这一块的内存空间用完了,就将还活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
缺点:
- 如果内存中多数对象都是存活的,这种算法将会产生大量的内存间复制的开销。
- 这种复制回收算法的代价时将可用内存缩小为了原来的一半
优点:
- 如果内存中多数对象都是可回收的情况,算法需要复制的就是占少数的存活对象,而且每次都是针对整个半区进行内存回收,分配内存时也就不用考虑有空间碎片的复杂情况,只要移动堆顶指针,按顺序分配即可。
- 这样实现简单,运行高效。
标记-整理算法
标记-复制算法在对子昂存活率较高时就要进行较多的复制操作,效率将会降低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。
针对老年代对象的存亡特征,提出了标记-整理算法,标记过程和“标记-清除”算法一样,但后续操作不是直接对可回收对象进行清理,而是让所有存活的对象都向内存空间一端移动,然后直接清理掉边界意以外的内存。
标记-清除算法与标记-整理算法的本质差异在于前者是一种非移动式的回收算法,而后者是移动式的。是否移动回收后的存活对象是一项优缺点并存的风险决策:
- 如果移动存货对象,尤其是在老年代这种每次回收都有大量对象存活区域,移动存活对象并更新所有引用这些对象的地方将会是一种极为负重的操作,而且这种对象移动操作必须全程暂停用户应用程序才能进行,这就更加让使用者不得不小心翼翼地权衡其弊端了,像这样的停顿被最初的虚拟机设计者形象的描述为“Stop The World”
- 如果跟标记-清除算法那样完全不考虑移动和整理存活对象的话,弥漫于堆中的存活对象导致的空间碎片化问题就只能依赖更为复杂的内存分配器和内存访问器来解决。譬如通过“分区空闲分配链表”来解决内存分配问题(计算机硬盘存储大文件就不要求物理连续的磁盘空间,能够在碎片化的硬盘上存储和访问就是通过硬盘分区表实现的)内存的访问时用户程序最频繁的操作,甚至没有之一,加入在这个环节上增加了额外的负担,势必会直接影响应用程序的吞吐量。
基于以上两点,是否移动对象都存在弊端,移动内存回收时会更复杂,不移动内存分配时惠更复杂。从垃圾收集的停顿时间来看,不移动对象停顿时间会更短,甚至不需要停顿;但从整个程序的吞吐量来看,移动对象会更划算。(注:吞吐量的实质是赋值器与收集器的效率总和)HotSpot虚拟机里面关注吞吐量的Parallel Scavenge收集器是基于标记-整理算法的,而关注延迟的CMS收集器则是基于标记-清除算法的。