概述
垃圾收集器是垃圾回收算法的具体实现,不同商家、不同版本的 JVM 所提供的垃圾收集器可能会有差别
收集器组合
JDK7后,HotSpot虚拟机中的7种垃圾收集器:Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS、G1。
所处区域
- 新生代收集器:Serial、ParNew、Parallel Scavenge
- 老年代收集器:Serial Old、Parallel Old、CMS
- 整堆收集器:G1
搭配
两个收集器间有连线,表明它们可以搭配使用
其中 Serial-Old 作为 CMS 出现 “Concurrent Mode Failure” 失败的后备预案
并发&并行
-
并行(Parallel)
指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态(ParNew、Parallel Scavenge、Parallel Old) -
并发(Concurrent)
指用户线程与垃圾收集线程同时执行(不一定是并行的,有可能会交替执行),用户程序在继续运行,而垃圾收集程序运行与另一个CPU上(CMS、G1)
Minor GC&Full GC
-
Minor GC
新生代GC,指发生在新生代的垃圾收集动作;因为Java对象大多是朝生夕灭,所以Minor GC非常频繁,一般回收速度也比较快 -
Full GC
Major GC或者老年代GC,指发生在老年代的GC;出现Full GC经常会伴随至少一次的Minor GC。
Major GC速度一般比Minor GC慢10倍以上
吞吐量(Throughput)
吞吐量是CPU用于运行用户代码的时间与CPU总消耗时间的比值
吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间)
收集器
Stop The World(GC停顿):JVM在后台自动发起和完成的,在用户不可见的情况下,把用户正常的工作线程全部停掉
Serial收集器
Serial(串行)垃圾收集器是最基本、发展历史最悠久的收集器。JDK1.3.1前是HotSpot新生代收集的唯一选择。
它是单线程收集器,只会使用一个CPU或者一条收集线程去完成垃圾收集工作,更重要的是它在进行垃圾收集时,必须暂停其他所有的工作线程,直至Serial收集器收集结束为止
特点
- 针对新生代
- 采用复制算法
- 单线程收集
- 会出现GC停顿
应用场景
依然是HotSpot在 Client 模式下默认的新生代收集器
设置参数
-XX:+UseSerialGC
ParNew收集器
ParNew垃圾收集器是 Serial 收集器的多线程版本。
除了使用多线程进行垃圾收集外,其余行为包括Serial收集器可用的所有控制参数、收集算法、Stop The World、对象分配规则、回收策略等与Serial收集器完全相同,两者共用了相当多的代码
特点
- 针对新生代
- 采用复制算法
- 多线程收集
- 会出现GC停顿
应用场景
在 Server 模式下,ParNew 收集器是一个非常重要的收集器,因为除 Serial 外,只有它能与CMS收集器配合
在单CPU环境中,不会比Serial收集器有更好的效果,因为存在线程交叉开销
设置参数
-XX:+UseConcMarkSweepGC 指定使用CMS后会默认使用ParNew作为新生代收集器
-XX:+UseParNewGC 强制指定使用ParNew
-XX:ParallelGCThreads 指定垃圾收集的线程数量,ParNew默认开启的收集线程与CPU的数量相同
Parallel Scavenge收集器
Parallel Scavenge收集器也是一个并行多线程新生代收集器。与其他收集器不同,Parallel Scavenge的目标是提高吞吐量,可以高效率地利用CPU时间,尽快完成程序的运算任务,适合在后台运算而不需要太多交互的任务。
Parallel Scavenge垃圾收集器因为与吞吐量关系密切,也称为吞吐量收集器(Throughput Collector)。
特点
- 针对新生代
- 采用复制算法
- 多线程收集
- 高吞吐量为目标
应用场景
对暂停时间没有特别高的要求时,即程序主要在后台进行计算,不需要与用户进行太多交互
例如:执行批量处理、订单处理、工资支付、科学计算等应用程序
设置参数
-XX:MaxGCPauseMillis 控制最大垃圾收集停顿时间,大于0的毫秒数
-XX:GCTimeRatio 设置垃圾收集时间占总时间比率 0 < n < 100的整数,相当于吞吐量大小
-XX:+UseAdptiveSizePolicy 开启这个参数后,就不用手工指定一些细节参数,如:-Xmn、-XX:SurvivorRation、-XX:PretenureSizeThreshold等
Serial Old收集器
Serial Old是Serial收集器的老年代版本,它同样是一个单线程收集器,使用“标记-整理”算法。
特点
- 针对老年代
- 采用 标记-整理 算法
- 单线程收集
应用场景
主要用于Client模式
Server模式有两大用途
- 在JDK1.5及之前,与Parallel Scavenge收集器搭配使用(JDK1.6有Parallel Old收集器可搭配)
- 作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用
设置参数
-XX:+UseSerialOldGC
Parallel Old收集器
Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。
特点
- 针对老年代
- 采用“标记-整理”算法
- 多线程收集
应用场景
JDK1.6及之后用来替代老年代Serial Old收集器
特别是在Server模式,多CPU的情况下
设置参数
-XX:+UseParallelOldGC
CMS收集器
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,它非常符合那些集中在互联网站或B/S系统的服务端上的JAVA应用,这些应用都非常重视服务的响应速度。
CMS收集器工作流程:
-
初始标记(CMS initial mark)
仅标记一下GC Roots能直接关联到的对象;
需要“Stop The World”,速度很快 -
并发标记(CMS concurrent mark)
进行GC Roots Tracing的过程;
上一步产生的集合中标记出存活对象,并不能保证可以标记出所有的存活对象;
应用程序也在运行,在整个过程中耗时最长 -
重新标记(CMS remark)
为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录;
需要“Stop The World”,且停顿时间比初始标记阶段稍长,但远比并发标记的时间短;
采用多线程并发执行来提升效率; -
并发清除(CMS concurrent sweep)
回收所有的垃圾对象
耗时最长的并发标记和并发清除过程中,收集线程都可以与用户线程一起工作,不需要进行停顿
特点
- 针对老年代
- 基于“标记-清除”算法
- 并发收集、低停顿
- 以获取最短回收停顿时间为目的
应用场景
与用户交互较多的场景
希望系统停顿时间较短,注重服务的响应速度
设置参数
-XX:+UseConcMarkSweepGC
缺点
- 对CPU资源非常敏感
并发收集虽然不会暂停用户线程,但因为占用一部分CPU资源,还是会导致应用程序变慢,总吞吐量降低
CMS默认收集线程数量 = ( CPU数量 + 3 ) / 4 - 无法处理浮动垃圾,可能出现“Concurrent Mode Failure”失败
- 浮动垃圾(Floating Garbage)
在并发清除时,用户线程新产生的垃圾称为浮动垃圾;这使得并发清除时需要预留一定的内存空间,不能像其他收集器在老年代几乎填满再进行收集
-XX:CMSInitiatingOccupancyFraction
设置CMS预留内存空间。JDK1.5默认68%,JDK1.6默认92% - Concurrent Mode Failure
如果CMS预留内存空间无法满足程序需要,就会出现一次“Concurrent Mode Failure”失败
此时JVM启用后备预案:临时启用Serial Old收集器,而导致另一次Full GC的产生
- 浮动垃圾(Floating Garbage)
- 产生大量内存碎片
由于CMS基于“标记-清除”算法,清除后不进行压缩操作,因此产生大量不连续的内存碎片,有可能提前触发另一次Full GC动作
-XX:+UseCMSCompactAtFullCollection
使得CMS出现上面情况时不进行Full GC,而开启内存碎片的合并整理过程。默认开启
-XX:+CMSFullGCsBeforeCompaction
设置执行多少次不压缩的Full GC后,来一次压缩整理。默认0
由于空间不再连续,CMS需要使用可用“空闲列表”内存分配方式
G1收集器
G1(Garbage-First)收集器是当今收集器技术发展最前沿的成果之一,是一款面向服务端应用的垃圾收集器。
G1收集器工作流程:
-
初始标记(Initial Marking)
仅标记一下GC Roots能直接关联到的对象;
修改TAMS(Next Top at Mark Start),让下一阶段并发运行时,用户程序能在正确可用的Region中创建新对象;
需要“Stop The World”,速度很快 -
并发标记(Concurrent Marking)
进行GC Roots Tracing的过程;
上一步产生的集合中标记出存活对象,并不能保证可以标记出所有的存活对象;
应用程序也在运行,在整个过程中耗时最长 -
最终标记(Final Marking)
为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录;
上一阶段对象的变化记录在线程的Remembered Set Log;这里把Remembered Set Log合并到Remembered Set中
需要“Stop The World”,且停顿时间比初始标记阶段稍长,但远比并发标记的时间短;
采用多线程并发执行来提升效率; -
筛选回收(Live Data Counting And Evacuation)
首先排序各个Region的回收价值和成本;
根据用户期望的GC停顿时间来定制回收计划;
按计划回收一些价值高的Region中垃圾对象;
回收时采用“复制”算法,从一个或多个Region复制存活对象到堆上的另一个空的Region,并且在此过程中压缩和释放内存
特点
- 并行和并发
能充分利用多CPU、多核环境下的硬件优势;
可以并行来缩短STW停顿时间;
也可以并发让垃圾收集和用户程序同时进行 - 分代收集,收集范围包括新生代和老年代
能独立管理整个GC堆(新生代&老年代),不需要与其他收集器搭配;
能够采用不用方式处理不同时期的对象; - 结合多种垃圾收集算法,空间整合,不产生碎片
整体看是基于“标记-整理算法”,局部(两个Region)看是基于复制算法 - 可预测的停顿,低停顿的同时实现高吞吐量
G1除了追求低停顿,还能建立可预测的停顿时间模型
应用场景
面向服务端应用,针对具有大内存、多处理器的机器
最主要的应用是为需要低GC延迟,并具有大堆的应用程序提供解决方案
设置参数
-XX:+UseG1GC 指定使用G1收集器
-XX:InitiatingHeapOccupancyPercent 当整个Java堆的占用率达到参数值时,开始并发标记阶段,默认45
-XX:MaxGCPauseMillis 为G1设置暂停时间目标,默认200毫秒
-XX:G1HeapRegionSize 设置每个Region大小,范围1M~32M;目标是在最小Java堆时可以拥有约2048个Region
网友评论