JVM(五、垃圾回收器)
经典的垃圾回收器大概有7种,这些收集器的目标、特性、原理、使用场景都有所区别,有的适用于年轻代,有的适用于老年代,图中展示的就是这7中垃圾回收器,如果两个垃圾回收器有连线,则表明可以配合使用。这个关系不是一成不变的,Serial+CMS、 ParNew+Serial Old本来也可以组合的,在JDK8、JDK9被声明废弃和取消。这篇文章主要讨论CMS、G1
1、Serial收集器
Serial收集器是最基础、历史最悠久的收集器,是一个单线程工作的收集器,单线程并不仅仅说明它只会使用一个处理器或者一条线程区完成垃圾收集工作,更重要的是它在垃圾回收时,必须暂停用户进程,Stop The World。
适用场景:客户端模式下新生代收集器
Serial虽然是单线程的,也是有优于其他收集器的地方,简单高效,对内存资源受限的环境,额外消耗内存最小的,所以适用于客户端模式的新生代。
2、ParNew收集器
ParNew是serial的多线程并行版本,除了使用多条线程进行垃圾收集之外,其他和Serial都一样。
使用场景:JDK7之前遗留系统的首选新生代收集器
其中有一个与功能、性能无关但其实很重要的原因是:除了Serial收集器外,目前只有它能与CMS 收集器配合工作。
3、Parallel Scavenge收集器
Parallel Scavenge收集器同样是新生代收集器,基于标记-复制算法实现,也是并行收集。Parallel Scavenge的目标是达到一个可控制的吞吐量
4、Serial Old收集器
Serial Old收集器是Serial的老年代版本,同样是单线程收集器,使用标记-整理算法。
使用场景:客户端模式
5、Parallel Old收集器
Parallel Old是Parallel Scavenge收集器的老年代版本,支持多线程并发收集,基于标记-整理算法。
6、CMS收集器
CMS收集器是一种以获取最短回收停顿时间为目标的收集器,目前集中运行在互联网网站或者基于浏览器的B/S系统的服务端上,这类应用关注服务的响应速度,希望系统停顿时间尽可能短,CMS适用这种场景。CMS基于标记-清除算法实现。
CMS收集过程四个步骤:
- 初始标记
- 并发标记
- 重新标记
- 并发清除
初始标记和重新标记需要Stop The World
初始标记:只是标记一下GC Root能直接关联的对象,速度很快。
并发标记:从GC Root能直接关联的对象开始,遍历整个引用链,这个过程虽然时间长,但是不需要停顿用户进程。
重新标记:修正并发标记期间,用户程序继续运行而导致编辑产生那一部分对象的标记记录,修正期间也是Stop The World。
并发清除:删除标记阶段判断已经死亡的对象,由于不需要移动存活对象,所以不需要停顿用户进程。
CMS优点:并发收集、低停顿。
7、G1收集器
在G1出现之前,其他的收集器目标范围都是整个新生代或者整个老年代,或者整个Java堆,而G1跳出了整个限制,面向堆内存任何部分来组成回收集进行回收,标准不再是哪个分代,而是那块内存中存放的垃圾数量最多,回收收益最大。
G1是基于Region的堆内存布局来实现任何内存部门来回收,把Java堆划分为多个大小相等的独立区域(Region),每个区域都可以根据需要扮演Eden、Survivor或者是老年代
Region中还有一类特殊的Humongous区域,专门存储大对象,只要超过一个Region容量的一半,就会判定为大对象,Region的大小可以通过参数-XX:G1HeapRegionSize设定,取值范围为1MB~32MB。
G1回收过程四个步骤:
- 初始标记 :仅仅只是标记一下GC Roots能直接关联到的对象,并且修改TAMS指针的值,让下一阶段用户线程并发运行时,能正确地在可用的Region中分配新对象。这个阶段需要停顿线程,但耗时很短,而且是借用进行Minor GC的时候同步完成的,所以G1收集器在这个阶段实际并没有额外的停顿。
- 并发标记:从GC Root开始对堆中对象进行可达性分析,递归扫描整个堆里的对象图,找出要回收的对象,这阶段耗时较长,但可与用户程序并发执行。当对象图扫描完成以后,还要重新处理SATB记录下的在并发时有引用变动的对象。
- 最终标记:对用户线程做另一个短暂的暂停,用于处理并发阶段结束后仍遗留下来的最后那少量的SATB记录。
- 筛选回收:负责更新Region的统计数据,对各个Region的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,可以自由选择任意多个Region构成回收集,然后把决定回收的那一部分Region的存活对象复制到空的Region中,再清理掉整个旧Region的全部空间。这里的操作涉及存活对象的移动,是必须暂停用户线程,由多条收集器线程并行完成的。