本章内容
- 标记-清除算法
- 复制算法
- 标记-整理算法
- 分代收集算法
- 七种垃圾收集器
标记-清除算法
标记;遍历所有的GC Roots,然后将所有GC Roots可达的对象标记为存活的对象
清除:清除的过程将遍历堆中所有的对象,将没有标记的对象全部清除掉
不足
1 效率问题 标记和清除两个过程的效率都不高(都需要遍历堆中所有的对象)
2 空间问题 标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后再程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作
复制算法(解决效率问题)
将可用内存按容量划分为大小相等的两块,每次只使用其中的一块,当一块的内存用完了,就将还存活的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉(每次对半区进行内存回收)
不足
将内存缩小为了原来的一半
标记-整理算法
标记:遍历所有的GC Roots,然后将所有GC Roots可达的对象标记为存活的对象
整理:将所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存
分代收集算法
根据对象的存活周期将内存划分为几块,一般将Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最合适的收集算法
新生代 每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选择复制算法,只需要付出少量存活对象的复制成本就可以完成收集
老年代 对象存活率高,没有额外空间对它进行分配担保,就必须使用标记-清理或标记-整理算法来进行回收
七种垃圾收集器
Young 代表新生代收集器,Tenured 代表老年代收集器,如果两个收集器之间存在联系,说明可以搭配使用
并行:指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态
并发:指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),用户程序在继续运行,而垃圾收集程序运行于另一个CPU上
注意:目前的收集器收集过程均需要进行停顿(但是因内存回收而导致停顿的时间一直在缩短)
以下这些收集器的特性,基本原理和使用场景
Serial收集器(单线程)
在进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束
虚拟机运行在Client模式下的默认新生代收集器(简单高效)
ParNew收集器(Serial多线程版本)
ParaLler Scavegne收集器(新生代收集器,使用复制算法,并行的多线程收集器)
目标:达到一个可控制的吞吐量
吞吐量:CPU用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量=运行用户代码时间/(运行用户代码时间 + 垃圾收集时间),虚拟机总共运行了100min,其中垃圾收集花费1min,吞吐量即为99%
高吞吐量可以高效率地利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务
Serial Old收集器
Seral收集器的老年代版本,单线程收集器,使用标记整理算法
用途
1 是在JDK1.5以及之前的版本中与Parallel Scavenge收集器搭配使用
2 作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用
Parallel Old收集器
Parallel Scavenge收集器的老年代版本,使用多线程和标记整理算法(Jdk1.6提供)
图为:Parallel Scavenge和 ParallelOld搭配使用
CMS收集器(标记-清除算法) 并发清除,低停顿
是一种以获取最短回收停顿时间为目标的收集器
算法过程(CMS收集器的内存回收过程是与用户线程一起并发执行的)
1 初始标记(Stop The Word)标记一下GC Roots能直接关联到的对象
2 并发标记 进行GC Root stracing(追踪)的过程
3 重新标记(Stop The Word) 为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段停顿时间一般会比初始标记阶段稍长,但远比并发标记的时间短
4 并发清除
缺点
1 对CPU资源非常敏感,在并发阶段,虽然不会导致用户线程停顿,但是会因为占用了一部分线程而导致应用程序变慢,总吞吐量会降低(默认启动的回收线程数是(CPU数量 + 3)/ 4)
2 CMS收集器无法处理浮动垃圾,可能出现Concurrent Mode Failure失败而导致另一次Full GC的产生,由于CMS并发清理阶段用户线程还在运行着,伴随着运行自然还会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后
3 使用的是标记-清除算法,收集结束后会有大量空间碎片产生
G1收集器