Serial
串行回收(STW)工作时要停止用户线程,单核CPU环境不需要线程间的切换,性能较高,使用的是标记清除算法
参数设置:-XX:+UseSerialGC,不需要设置老年代,老年代会自动搭配Serial Old使用
Serial Old
串行回收(STW)工作时要停止用户线程,使用的是标记压缩算法,是Client模式下默认的老年代垃圾回收器
ParNew
并行回收,是Serial的多线程的版本,暂停时间较Serial短,适合处理新生代的垃圾回收器,可以搭配CMS/Serial Old老年代垃圾回收器使用<br>
参数设置
jdk8:-XX:+UseParNewGC,老年代默认搭配Serial Old使用
jdk9:-XX:+UseParNewGC -XX:+UseConcMarkSweepGC,在jdk9以及之后只能搭配老年代回收器CMS使用
Parallel
并行回收,吞吐量优先(目标是达到一个可控制的吞吐量),自适应的调节策略(内存分配调节),适用于不需要与用户进行过多交互的服务端后台程序(订单处理等)
参数设置
-XX:+UseParallelGC,默认搭配老年代收集器Parallel Old使用
-XX:+UseParallelOldGC,使用老年代的收集器,JDK8是默认开启的
-XX:ParallelGCThreads,设置年轻代并行垃圾收集器的线程数,一般设置与CPU的核心数相同
-XX:MaxGCPauseMills,设置最大的垃圾回收时的停顿时间(STW),慎用
-XX:GCTimeRatio,设置垃圾回收占总时间的比例
-XX:+UseAdaptiveSizePolicy,自适应调节策略(堆内存分配),默认是开启的
Parallel Old
jdk6中出现,替换了Serial Old作为老年代的回收,提高了吞吐量,适用于Server服务端<br>
CMS
响应时间(低延迟),JDK5出现是第一款真正意义上的并发的垃圾收集器,尽可能的缩短垃圾收集线程工作时用户线程停顿的时间,使用的是标记清除算法
工作原理
初始标记
仅仅只是标记出GC Roots能直接关联的对象,执行速度非常快,会出现很短的STW时间
并发标记
并发执行,根据GC Roots来遍历整个对象图关联可达对象的过程,耗时较长,因为是与用户线程并发执行的,所以会出现对象标记的变化<br>
1.标记为可达的对象,但是在用户线程执行中对象引用被释放,由于这部分对象已经被标记,所以在本次垃圾回收将无法被回收,变成浮动垃圾
2.不可达的对象,但是在用户线程执行中又有了引用关系,这部分对象将会在重新标记阶段被标记出来,避免被回收
重新标记
修正第一次标记期间到此时间这一段时间内产生变化的标记对象,也会有较短的STW时间(比第一阶段时间长一点),使用的是增量更新算法<br>
并发清理<br>
并发执行,清理删除掉标记的已死亡的对象,释放内存空间(使用的是标记清理算法)
注意事项
在CMS进行垃圾收集的同时,用户线程也是在同时工作的,因此要确保有一定的内存空间可以供用户线程工作使用<br>
所以CMS并不能像其他垃圾收集器一样等老年代爆满了才进行垃圾收集,而是当堆内存使用率达到一定的阈值时就开始进行垃圾收集工作,<br>
以确保用户线程在CMS进行垃圾收集的时候还可以进行正常的工作。如果CMS在运行期间剩余的空间无法满足用户线程的工作,<br>
就会出现一次“Concurrent Mode Failure”,这时虚拟机将启用备用方案Serial Old进行垃圾回收,会增加垃圾收集的时间开销,这也就是CMS+Serial Old组合的老年代收集<br>
性能分析
缺点
会产生内存碎片,CMS对处理器性能十分敏感,无法处理浮动垃圾(并发标记阶段产生的新的垃圾对象,将不能被标记,被称为浮动垃圾)<br>
参数设置
-XX:+UseConcMarkSweepGC,使用CMS垃圾收集器,开启此参数时会自动开启-XX:+UseParNewGC作为新生代的垃圾回收器
-XX:CMSInitiatingOccupanyFraction,设置堆内存使用率的阈值,一旦达到该阈值就会触发CMS进行垃圾收集工作,JDK6以及以上默认是92%<br>
-XX:ParallelCMSThreads,设置垃圾收集器的并发工作的线程数,默认情况是( (ParallelGCThreads+3)/4 )<br>
G1
简述
JDK9以及之后默认的垃圾收集器,是一款面向服务端的垃圾收集器,主要针对多核CPU以及大容量内存的机器,以极高概率满足GC停顿时间的同时还具有较高的吞吐量<br>
将堆空间分为一个个的Ragion,G1通过跟踪Ragion中垃圾回收价值的大小(回收所获得的空间大小以及回收所需要的时间),在后台维护一个优先级列表,每次根据允许的收集时间,优先回收价值最大的Ragion
特点
并行与并发
并行性:G1在回收期间,可以有多个垃圾回收线程同时工作,有效利用多核CPU的性能,此时用户线程是STW的,(年轻代的收集)<br>
并发性:G1拥有与用户线程交替执行的能力,部分工作可以和用户线程同时进行,因此,不会在整个回收阶段都发生阻塞用户线程的情况(堆内存占用达到堆使用阈值后开始并发标记,准备对老年代的回收,默认堆占用率是45%)<br>
分代收集
G1依然是属于分代型的垃圾收集器,但是它不在要求年轻代老年代的固定大小与固定数量<br>
将整个堆分为一个个的Ragion,这些Ragion包含了逻辑上的年轻代与老年代,同时兼顾老年代年轻代的收集<br>
空间整合
Ragion之间使用的是“标记-复制”算法,整体上是“标记-压缩”算法,都避免了空间碎片的问题<br>
当堆空间越大的时候,G1的优势就越加明显<br>
可预测的停顿时间模型
每次根据允许的收集时间,有限回收价值最大的Ragion(一个或多个),获取尽可能高的收集效率<br>
Ragion
将整个堆划分为约2048个Ragion区域,每个Ragion的大小根据堆空间的大小而定,整体被控制在1-32M,且是2的次幂,即可为1,2,4,8,16,32,可以通过参数进行设置,所有的Ragion大小相同,且在jvm生命周期内不会改变,堆空间大小(2G-64G)<br>
还保留着新生代老年代的定义,但是他们不再是物理上连续的区域了,而是通过Ragion集合的形式存在<br>
Ragion分为四类:E(Eden对象),S(Survivor对象),O(Old对象),H(Humongous当对象大小超过1.5个Ragion大小时被称为巨型对象或者大对象,分配空间可以占用多个连续的Ragion区域,如果找不到多个连续的Ragion区域,则触发full gc进行空间整理)<br>
Ragion中解决隔代引用的问题是使用记忆集的方式,记录不同代之间的相互引用,记忆集是一个抽象的数据结构,Hotspot使用的实现是卡表来进行实现的(字节数组),占用约每个Ragion的10-20%的空间
参数设置
-XX:+UseG1GC,启用G1垃圾收集器<br>
-XX:G1HeapRegionSize,设置Region的大小,值是2的次幂,范围在1M-32M,目标是根据最小的java堆划分出约2048个区域,默认是堆内存的1/2000<br>
-XX:MaxGCPauseMillis,设置期望最大的GC停顿时间(JVM会尽力实现,但不保证),系统默认是200ms<br>
-XX:ParallelGCThreads,设置GC并行回收垃圾时工作的线程数(用户线程有STW),最大设置为8<br>
-XX:ConcGCThreads,设置并发标记的线程数,为ParallelGCThreads的1/4左右<br>
-XX:InitiatingHeapOccupancyPercent,设置触发并发GC周期的java堆占用阈值,超过就触发GC,默认大小是45<br>
工作原理
初始标记
标记出GC Roots,修改TAMS指针的值,让下一阶段用户线程并发运行时,能正确地在可用的Region中分配新对象。
需要停顿用户线程,但停顿时间较短<br>
并发标记
从GC Root开始对堆中对象进行可达性分析,扫描整个堆中的对象图,找出要回收的对象<br>
这阶段耗时较长,但可与用户程序并发执行,当对象图扫描完成以 后,还要重新处理SATB(原始快照)记录下的在并发时有引用变动的对象
最终标记
对用户线程做另一个短暂的暂停,用于处理并发阶段结束后仍遗留下来的最后那少量的SATB记录(原始快照SATB)<br>
筛选回收
更新Region的统计数据,对各个Region的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,
可以自由选择任意多个Region 构成回收集,然后把决定回收的那一部分Region的存活对象复制到空的Region中,再清理掉整个旧 Region的全部空间
这里的操作涉及存活对象的移动,是必须暂停用户线程,由多条收集器线程并行完成,停顿时间较短
Full GC
可能发生,当在一定的时间内回收的空间跟不上使用的空间时,空间即将耗尽,就会触发full GC(单线程,STW时间也较长),影响程序的性能