垃圾收集算法
垃圾收集算法可划分为“引用计数式垃圾收集”和“追踪式垃圾收集”两大类,这两类也被称为“直接垃圾收集”和“间接垃圾收集”。以下全为“追踪式垃圾收集”。
一、分代收集理论
- 弱分代假说:绝大多数对象都是朝生夕灭的。
- 强分代假说:熬过越多次垃圾收集过程的对象就越难以消亡。
- 跨代引用假说:跨代引用相对于同代引用来说占极少数。
前两个假说奠定了常用垃圾收集器的设计原则:收集器应该将Java堆划分出不同的区域,然后将回收对象依据其年龄(年龄即熬过垃圾收集过程的次数)分配到不同的区域之中存储。
为什么要引入第三条经验法则:如果要进行一次只局限于新生代区域内的收集,但新生代中你的对象是完全有可能被老年代所引用的,为了找出存活的对象,就需要额外遍历整个老年代中的所有对象,这会给内存回收带来很大的性能负担。
部分收集(Partial GC):指目标不是完整收集整个Java堆的垃圾收集。
- 新生代收集(Minor GC/Young GC):指目标只是新生代的收集。
- 老年代收集(Major GC/Old GC):指目标只是老年代的垃圾收集。
- 混合收集(Mixed GC):指目标是收集整个新生代以及部分老年代的垃圾收集。
整堆收集(Full GC):收集整个Java堆和方法区的垃圾收集。
二、标记-清除算法
标记-清除(Mark-Sweep)算法分为“标记”和“清除”两个阶段:首先标记出所有存活的对象,统一回收所有未被标记的对象。
缺点:
- 执行效率不稳定
- 内存空间的碎片化问题
如图,深色为可回收部分,当回收后,深色部分变为未使用状态;回收后未使用区块有很多不连续的,即产生了大量的不连续的内存碎片,以后如果程序需要分配较大对象时无法找到足够的连续内存,就要提前触发垃圾收集动作。
三、标记-复制算法
标记-复制(Mark-Copy)算法将可用内存按容量划分为大小相等的两块,每次只是用其中一块。当这一块的内存用完了,就将还存活的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
优点:
- 分配内存时不需要考虑空间碎片的复杂情况,只需要按顺序分配即可
缺点:
- 如果大多数对象都是存活的,会产生大量内存间复制的开销
- 该复制短发将内存缩小为原来的一半,空间浪费过大
四、标记-整理算法
标记-整理(Mark-Compact)算法,先对对象进行标记,然后所有存活的对象都向内存空间一端移动,然后直接清理掉边界以外的内存。
优点:
- 内存分配时较为简单,由于内存分配和访问相比垃圾收集的频率要高得多,因此不移动对象总吞吐量会高一些。
缺点:
- 内存回收更为复杂
- 移动存活对象停顿时间会更长(这个停顿是全程暂停用户应用程序)