垃圾收集器
Java虚拟机对垃圾收集器没有明确规范,不同厂商都会针对自己的应用特点和需求组合出各个年代所
使用的收集器。JDK1.7 Update 14之后的HotSpot虚拟机所包含的收集器如下,如果两个收集器之间
存在连线,就说明他们可以搭配使用,收集器所处的区域代表它属于新生代还是老年代。借图:
1、Serial收集器
Serial是一个单线程收集器,在他进行收集时,必须暂停其他所有的工作线程,它是虚拟机运行在
Client模式下的默认新生代收集器,简单高效。借图:
2、ParNew收集器
ParNew是Serial的多线程版本,它是许多运行在Server模式下的虚拟机中首选的新生代收集器,因为
除了Serial以外只有它能与CMS收集器配合工作。ParNew也是使用-XX:+UseConcMarkSweepGC
选项后的默认新生代收集器,也可以使用-XX:UseParNewGC选项来强制指定它。它默认开启的收
集线程数与CPU数量相同,在CPU非常多的情况下,可以使用-XX:ParallelGCThreads参数来限制
垃圾收集的线程数。借图:
3、Parallel Scavenge收集器
Parallel Scavenge与ParNew一样是使用复制算法并行的多线程收集器,但它的特点是关注吞吐量
(Throughput),其他的收集器关注的是缩短垃圾收集时用户线程的停顿时间。吞吐量就是CPU用
于运行用户代码的时间与CPU总消耗时间的比值,例如虚拟机总运行时间100分钟,其中垃圾收集消
耗1分钟,那么吞吐量就是99%。停顿时间短适合交互频繁的场景,高吞吐量则适合更多的后台运算
需求。Parallel Scavenge提供了几个参数用于精确控制吞吐量:
① -XX:MaxGCPauseMillis是控制最大垃圾收集停顿时间的,允许的值是一个大于0的毫秒
数,收集器将尽可能的保证回收时间低于设定值,但它是靠牺牲吞吐量和新生代空间而换取的
,新生代空间被压低,回收时间虽然变短,但是却更加频繁,导致吞吐量下降。
② -XX:GCTimeRatio是直接设置吞吐量大小的,参数值是大于0小于100的整数,默认值
为99,也就是允许最大1%的垃圾收集时间。
③ -XX:UseAdaptiveSizePolicy:开关参数,打开后就不需要手工指定新生代的大小(-
Xmn)、Eden与Survivor区的比例(-XX:SurvivorRatio)、晋升老年代对象大小(-XX:
PretenureSizeThreshold)等细节参数。虚拟机会根据当前运行情况动态调整,这种调节方式
称为GC自适应调节策略(GC Ergonomics)。
4、Serial Old收集器
Serial Old是Serial的老年代版本,是单线程收集器,使用“标记-整理”算法。主要也是给Client模式
下的虚拟机使用的,如果在Server模式下,他有另外两大用途:一是在JDK1.5以及之前的版本中与
Parallel Scavenge收集器搭配使用,二是作为CMS收集器的后备预案,在并发收集发生Concurrent
Mode Failure时使用,Serial Old工作图见Serial收集器配图。有关并行和并发的区别如下:
① 并行:Parallen,指多条垃圾收集线程并行工作,此时用户线程处于等待状态
② 并发:Concurrent,指用户线程与回收线程同时进行,不一定是并行,可能是交替执行,
用户线程没有中断,垃圾收集程序运行在另一个CPU上
5、Parallel Old收集器
Parallel Old是Parallel Scavenge的老年代版本,使用多线程和“标记-整理”算法。在JDK1.6版本以
前,新生代Parallel Scavenge由于只能和老年代Serial Old收集器搭配,导致整体性能并没有最大化
,可能还不如ParNew+CMS的组合效果好。现在在注重吞吐量以及CPU资源敏感的场合,可以使用
Parallel Scavenge+Parallel Old的组合了。借图:
6、CMS收集器
CMS(Concurrent Mark Sweep)是以获取最短回收停顿时间为目标的收集器。在很多重视服务响
应速度、希望系统停顿时间最短的服务端上常用这个收集器。它基于“标记-清除”算法,工作分4个
步骤:
① 初始标记(CMS initial mark)
② 并发标记(CMS concurrent mark)
③ 重新标记(CMS remark)
④ 并发清除(CMS concurrent sweep)
初始标记和重新标记需要“Stop The World”。初始标记是标记GC Roots直接关联的对象,速度很快,
并发标记是进行GC Roots Tracing的过程,重新标记是为了修正并发标记期间因用户程序运作而导
致标记产生变动的那一部分对象的标记记录,停顿时间比初始标记略长,但比并发标记时间短的多。
过程中耗时最长的并发标记和并发清除都可以与用户线程并发工作。借图:
Sun官方文档称CMS为并发低停顿收集器(Concurrent Low Pause Collector),但它还有3个明显
的缺点:
① CMS对CPU资源非常敏感,虽然不会导致用户线程停顿,但因为占用了一部分线程而导致
应用程序变慢,总吞吐量降低。CMS默认启动回收线程数是(CPU数量+3)/4,也就是在CP
U在4个以上时,并发回收线程不少于25%的CPU资源,随CPU数量增加而下降。但CPU数量
更少的时候占用的资源就会很大,所以虚拟机提供了一种称为“增量式并发收集器”
(Incremental Concurrent Mark Sweep/i-CMS)的CMS收集器变种,就是在并发标记、清
理的时候让GC和用户线程交替进行,但效果一般,已经被弃用。
② CMS无法处理浮动垃圾(Floating Garbage),可能出现“Concurrent Mode Failure”失败
导致另一次Full GC的产生。在并发清理过程中,并行产生的用户垃圾称之为浮动垃圾。所以
CMS不能等老年代几乎被填满了以后再进行收集,要预留一部分空间留给浮动垃圾。在JDK1.
5的默认设置下,CMS当老年代使用了68%后就会被激活,可以用-XX:
CMSInitiatingOccupancyFraction来调整这个触发比例。在JDK1.6中已经提高到了92%,如
果预留空间无法满足浮动垃圾的需求,会触发“Concurrent Mode Failure”,启动后备预案,
使用Serial Old来进行老年代的垃圾收集。
③ CMS基于“标记-清除”算法,会产生大量内存碎片,导致无法找到足够大的连续空间来分
配,提前触发Full GC,所以CMS提供了-XX:UseCMSCompactAtFullCollection开关(默认
开启)来让CMS在Full GC前进行一次内存碎片的整理,内存整理无法并发完成,所以停顿时
间变长。还有一个-XX:CMSFullGCsBeforeCompaction参数用来设置执行多少次不压缩的
Full GC后跟着来一次带压缩的(默认为0,表示每次都要整理)。
7、G1收集器
G1(Garbage-First)收集器是一款面向服务端应用的垃圾收集器,与其他收集器相比他有以下几个
特点:
① 并行与并发:充分利用多CPU、多核资源来缩短“Stop The World”时间,可以使用并发的
方式使原本需要停顿的动作继续执行。
② 分代收集:G1可以自己管理各个年代并使用不同的处理方式分别对他们进行垃圾回收。
③ 空间整合:G1从整体上看是基于“标记-整理”算法实现的,从局部(两个Region之间)看
是基于“复制”算法的。所以他不会产生内存空间碎片。
④ 可预测的停顿:G1可以明确指定在一个长度M毫秒的时间内,消耗在垃圾收集的时间不超
过N毫秒,具备了实时Java(RTSJ)的垃圾收集器特征。
G1收集器会将内存划分为多个大小相等的独立区域(Region),虽然保留了新生代与老年代的区分
,但他们没有物理隔离,只是一部分Region的集合罢了。它跟踪各个Region里垃圾堆积的价值大小
(回收所获得的空间大小以及回收所需时间的经验值),在后台维护一个列表,每次根据允许的收集
时间优先回收价值最大的Region(Grabage-First名字的由来),保证G1可以有计划的避免在整个
Java堆进行垃圾回收,在有限的时间内获取尽可能高的收集效率。
在G1收集器中,Region之间互相引用导致回收时需要全堆扫描的问题比较严重,与其他收集器一样
,G1使用的也是Remembered Set来避免这个问题的。每个Region都有一个与之对应的
Remembered Set,虚拟机发现程序在对Reference类型数据进行写操作时,会产生Write Barrier暂
时中断写操作,检查Reference引用的对象是否处于不同的Region之中,如果是则通过CardTable把
相关引用信息记录到被引用对象所属的Region的Remembered Set中,当垃圾回收时,在GC根节点
的枚举范围中加入Remembered Set即可。
如果不计算维护Remembered Set的操作,G1的运作可分为以下几个步骤,借图:
① 初始标记(Initial Marking),标记GC Roots能直接关联到的对象,并修改TAMS(Next
Top at Mark Start)的值,让下一阶段用户程序并发运行时可以正确使用可用的Region创建
新对象,该过程需要停顿,但耗时很短。
② 并发标记(Concurrent Marking),从GC Roots开始对堆中对象进行可达性分析,找出存
活对象,耗时较长,可以并发进行。
③ 最终标记(Final Marking),修正在并发标记期间导致标记产生变动的那一部分标记记录
,虚拟机将这段时间内的变化记录在线程Remembere Set Logs中,在最终标记时把
Remembered Set Logs数据合并到Rememberd Set中,该阶段需要停顿,可并发执行。
④ 筛选回收(Live Data Counting and Evacuation),首先对各个Region的回收价值和成本
价值进行排序,根据用户期望的停顿时间定制回收计划,可以并发执行,但因为回收的只是一
部分Region,时间也是用户控制,所以停顿用户线程会大幅提高收集效率。