垃圾收集器
概述
如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。
图3-5展示了7种作用于不同分代的收集器,如果两个收集器之间存在连线,那么就说明他们可以搭配使用。虚拟机所在的区域则表示它是属于新生代收集器还是老年代收集器
serial收集器
serial收集器是最基本、发展历史最悠久的收集器,曾经是JDK1.3之前新生代收集器的唯一选择。他是一个单线程的收集器,在进行垃圾收集的时候必须暂停其他所有的工作线程,直到他收集结束。
Parnew收集器
Parnew就是Serial收集器的多线程版本。在单CPU的情况下由于存在线程的交互开销,所以他的性能不一定比Serial好。但是随着CPU数量的增加,他对于GC时系统资源有效利用还是很不错的。
从Parnew收集器开始包括后面介绍的收集器,都被称为并发和并行的收集器
1-并行(Parallel):指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态
2-并发(Concurrent):指用户线程与垃圾收集器同时执行(但不一定是并行的,可能会交替执行),用户程序在继续运行,而垃圾收集程序运行在另一个CPU上。
Parallel Scavenge收集器
Parallel Scavenge新生代收集器,使用的复制算法。他的关注点和其他收集器不同,CMS等收集器关注点是尽可能缩短垃圾收集时用户线程的停顿时间,而他主要目标是达到一个 可控制的吞吐量。**吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)**假设虚拟机总共运行了100分钟,其中垃圾收集器花掉1分钟,那么吞吐量就是99%。
Parallel Scavenge还有一个参数-XX:+UserAdaptiveSizePolicy值得关注,这个参数打开之后就不需要手工指定新生代的大小(-Xmn)、晋升老年代对象年龄(-XX:PretenureSizeThreshold)等细节参数,虚拟机会根据系统的运行情况收集性能监测信息,动态调整这些参数一提供最合适的停顿时间或者最大吞吐量,这种调节方式成为 GC自适应的调节策略(GC Ergonomics)这个是与ParNew收集器的一个重要区别。
Serial Old收集器
Serial Old是Serial收集器的老年代版本,他也是一个单线程收集器,使用“标记-整理”算法。主要的意义也是给Client模式下的虚拟机使用。如果在Server模式下他有两大用途:一:在JDK1.5及之前的版本中与Parallel Scavenge收集器搭配使用,二:作为CMS收集器的后备方案,并在Concurrent Mode Failure时使用
Parallel Old收集器
Parallel Old是Parallel Scavenge收集器的老年版本,使用多线程和“标记整理”算法,在1.6才开始提供的。在此之前Parallel Scavenge只能和Serial Old搭配,无法和其他收集器搭配。由于老年代Serial Old在服务性能的“拖累”,由于单线程老年代收集无法充分利用服务器多CPU的处理能力,所以导致“吞吐量优先”的收集器也名副其实
CMS(Concurrent Mark Sweep)收集器
这是一款以获取最短回收停顿时间为目标的收集器。它是一款基于“标记-清除”算法实现的,运行过程相对以上几种相对来说更加复杂,大致可以分为以下四个步骤
- 初始标记(CMS initial mark)
- 并发标记(CMS Concurrent mark)
- 重新标记(CMS remark)
- 并发清除(CMS Concurrent sweep)
初始标记和并发标记这两个步骤仍然需要“Stop The World”,初始标记仅仅只是标记以下GC Root能直接关联到的对象,速度很快,并发标记就是进行GC Root Tracing的过程。重新标记阶段则是为了修正并发标记期间因用户程序继续运作而导致标记变动的那一部分对象的标记记录,这个阶段停顿的时间一般会比初始标记稍长一点但远比并发标记的时间短。
缺点:- 对CPU资源非常的敏感,在并发阶段虽然不会导致用户线程停顿,但是会因为占用了一部分线程(或者说CPU资源资源)而导致总吞吐量会降低
- CMS无法处理浮动浮动垃圾(CMS并发收集垃圾的时候,还会有新的垃圾产生。CMS会在下一次收集),可能出现“Concurrent Mode Failure”失败而导致另一次Full GC的产生
- 会产生大量空间碎片
G1收集器
G1之前的其他收集器范围都是整个新生代或者老年代,而G1不再是这样。G1收集器的堆内存布局和其他的有很大的区别,它将整个Java堆划分为多个大小相等的独立区域。虽然保留新生代和老年代的概念,但不再是物理隔离了。G1是一款服务端应用的垃圾收集器,与其他收集器相比他有如下的特点
- 并行与并发:充分利用多cpu,能够缩短Stop-The_world停顿的时间
- 分代收集:采取不同的策略去收集存活时间不同的对象
- 空间整合:从整体上看是基于“标记-整理”算法实现的收集器,从局部(两个Region之间)上看是基于“复制算法实现的”。不管哪种算法都不会产生空间碎片
- 可预测的停顿:除了追求低停顿之外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段,消耗在垃圾收集器上的时间不超过N毫秒
G1能够预测的停顿是因为追踪Region里面的垃圾堆积的价值大小(回收空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根据所允许的收集时间优先回收价值最大的的Region.
G1化整为零的思路理解起来很容易但实现起来却非常困难,否则也不会再2004年sun实验室发表第一篇G1论文到现在在开发出商用版。因为每个Region不是孤立的,他可能被其他区域的对象所引用,那做可达性分析的时候岂不是还得扫描整个java堆才能保证准确性。虚拟机都是采用Remembered Set来避免全堆扫描。G1中每个Region都有一个对应的Remembered Se表。不计算维护Remenbered Set的操作,G1运行大致可以分为以下四个内容
- 初始标记(initial Marking)
- 并发标记(Concurrent Marking)
- 最终标记(Final Marking)
- 筛选回收(Live Data Counting and Evacuation)
垃圾收集器参数总结