垃圾回收器
分类
按线程数分,可分为串行垃圾回收器和并行垃圾回收器
按照多个模式分,并发式垃圾回收器和独占式垃圾回收器
- 并发式:与应用程序接替工作,减少应用程序的停顿时间
- 独占式:一旦运行就停止所有用户线程,直到垃圾回收过程结束
按照碎片处理方式,压缩式垃圾回收器和非压缩式垃圾回收器
- 压缩式:会在回收完后对存活对象进行压缩整理,消除回收碎片
- 非压缩式:无上步骤
按照工作内存区间,年轻代回收器和老年代回收期
- 新生代:Serial、ParNew、Parallel Scavenge
- 老年代:Serial Old、Parallel Old、CMS
- 整堆:G1
查看默认的垃圾回收器
- -XX:+PrintCommandLineFlags:查看命令行相关参数(包括使用的垃圾回收器)
- 使用命令行指令:jinfo -flag 相关垃圾回收期参数 进程ID
Serial回收器:串行回收
Serial回收器是最基本,历史最悠久的回收器,JDK1.3之前回收新时代的唯一选择
Serial使用复制算法、串行回收和"Stop-the-World"机制
Serial Old是针对老年代的垃圾回收器,使用标记压缩算法
Serial Old在Serial模式下有两个主要作用:1、与新生代的Parallel Scavenge配合使用。2、作为老年代收集器CMS的后备方案
Serial是一个单线程垃圾回收器,它只使用一个CPU或者一条收集线程去完成垃圾收集工作,而且在垃圾收集时,其他的线程必须都停止(Stop-the-World)
在HotSpot虚拟机中,使用 -XX:+UseSerialGC指定垃圾回收器
无 -XX:+UseSerialOldGC 参数,使用Serial默认使用Serial Old
ParNew回收器:并行回收
ParNew就类型与Serial的多线程版本
Par是Parallel的缩写,New:表示只处理新生代
ParNew除了使用并行回收之外,几乎和Serial没有任何区别
ParNew是很多JVM运行在Server模式下新时代的默认垃圾回收器
- 设置垃圾回收器:-XX:+UseParNewGC
- -XX:ParallelGCTreads 限制线程数 (默认与CPU数据相同)
Parallel Scavenge回收器:吞吐量优秀
JDK8的默认收集器
Parallel Scavenge也采用了复制算法、并行回收和"Stop-the-World"
- 和ParNew不同的是,该回收器的目标是达到一个可控的吞吐量,它也被称为吞吐量优先垃圾回收器
- 自适应调节策略也是Parallel Scavenge与ParNew的一个重要区别
自适应调节策略:在JVM运行过程当中,根据当前运行的情况,进行一个动态的监控,可以动态的调整内存的分配情况,达到一个最优的策略
高吞吐量可以更高效率的利用CPU时间,主要适合在后台运算而不需要太多交互的任务,多在服务器环境中使用
Parallel在JDK1.6时提供Parallel Old供老年代使用,Parallel Old使用标记压缩算法,同样也是并行回收和"Stop-the-World"
参数设置
- -XX:+UseParallelGC 手动指定新生代使用ParallelGC
- -XX:+UserParallelOldGC 手动指定老年代使用 (这两个参数是相互激活的,一个使用另外一个默认使用)
- -XX:ParallelGCTreads 设置新生代并行回收器线程个数
- 默认情况下:CPU个数小于8个,ParallelGCTreads值为CPU个数
- CPU个数大于8时,ParallelGCTreads=3+[(5+CPU_count)/8]
- -XX:MaxGCPauseMillis 设置垃圾回收器最大停顿时间(毫秒)(回收器为了控制时间,会调整堆的大小或者一些其他参数)
- -XX:GCTimeRatio 垃圾回收占总时间的比例 (与上一个参数相互矛盾,暂停时间越长,Ratio越容易超出设置的比例)
- -XX:UseAdaptiveSizePolicy 设置自适应调节策略
CMS(Concurrent-Mark-Sweep)回收器
HotSpot虚拟机中第一款真正意义上的并发垃圾回收器,第一次实现了垃圾收集线程与用户线程同时工作
CMS关注点是尽可能缩短垃圾收集时用户线程的停顿时间,停顿时间越短,就越适合与用户交互的程序
采用标记清除算法,“Stop-the-World”
CMS只能和和ParNew 和 Serial来配合使用
CMS主要分为4个阶段:初始标记、并发标记、重新标记、并发清除
- 初始标记:这个阶段,所有工作线程都会因"Stop-the-World"出现短暂的暂停,这个阶段仅仅标记出GC Root能直接管理到的对象,由于直接关联到的对象较少,所以速度较快
- 并发标记:从GC Roots直接关联到对象开始遍历整个对象图的过程,耗时较长,但是不需要停顿
- 重新标记:在并发阶段,因重新运行对标记产生变动的对象进行标记记录
- 并发清除:清理所有的标记阶段已经死亡的对象
由于CMS在回收过程当中,用户线程没有中断,还需要保证程序需要有足够的线程可以使用。不可以像其他的垃圾回收器一样等老年代几乎被填满再回收。当堆内存使用达到某一阈值就开始回收
要是在回收过程当中,预留空间不够使用,则虚拟机临时启动Serial Old来回收
- 优点
- 缺点
- 会产生内存碎片
- 对CPU资源非常敏感,会降低吞吐量
- 无法处理浮动垃圾,在并发标记阶段,如果产生新的连接,CMS无法对这些新的垃圾无法标记
参数设置
- -XX:+UseConcMarkSweepGC 手动打开CMS(打开时会和ParNewGC一起打开)
- -XX:CMSlnitiatingOccupanyFraction 设置堆内存使用阈值
- -XX:+UseCMSCompactAtFullCollection 用于指定在执行完Full GC后对内存空间进行压缩整理
- -XX:CMSFullGCsBeforeCompaction 设置执行多少次Full GC后进行整理
- -XX:ParallelCMSThreads 设置CMS线程数量 默认线程数为 (ParallelGCThreads+3)/4
G1回收器:区域化分代式
JDK9之后的默认的垃圾回收器
G1(Garbage-First)是一个并行回收器,它把堆内存分割成很多不想管的区域(物理上不连续)。使用不同的Region来表示Eden、幸存者0区、幸存者1区、老年代等
G1 GC有计划的避免在整个Java堆中进行全区域的进行垃圾回收。G1跟踪各个Region里的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需时间的经验值),在后台维护一个优先级列表,每次根据运行的收集时间,优先回收价值最大的Region
G1是一款面向服务端应用的垃圾回收器,主要针对配备多核CPU和大容量内存的机器,以极高的效率满足GC停顿时间的同时,还兼具高吞吐量性能的特征
优点
-
并行与并发
- 并行:G1在回收期间,有多个GC线程同时工作,有效利用多核计算能力,此时用户线程STW
- 并发:G1运用与应用程序交替执行的功能,部分工作可以和应用程序同时执行,因此,不会再整个回收阶段发生完全阻塞应用程序的情况
-
分代收集
- G1仍属于分代型垃圾回收器,会区分年轻代和老年代,年轻代依然有Eden和Survivor区,但是它不要求整个年轻代或者老年区是连续的,也不坚持固定大小和数量
- 将堆空间分为若干个Region,这些区域逻辑上包含老年代和年轻代
- 有可以作用与年轻代也可以作用于老年代
-
空间整合
- G1将空间划分为一个一个的Region,内存回收是以Region为基本单位的。Region之间是复制算法,但是整体可以看作标记压缩算法
-
可预测的时间停顿模型
- G1除了追求低停顿之外,还能建立可预测的停顿时间模型,能够让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不超过N
缺点
- 相比于CMS,G1在程序运行过程中,为了垃圾回收产生的内存占用还是程序运行时的额外执行负载都要比CMS高
- 从经验上来说,在小内存应用上CMS大概率优与G1,而G1在6-8GB发挥其优势
相关参数
- -XX:UseG1GC 手动指定回收器
- -XX:G1HeapRegionSize 设置每个Region的值大小,值是2的幂,范围是1MB~32MB,目标是根据最小的Java堆大小划分2048个区域,默认是堆内存的1/2000
- -XX:MaxGCPauseMillis 设置期望达到的最大GC停顿时间,默认200ms
- -XX:ParallelGCTreads 设置STWGC程数的值,最大为8
- -XX:ConcGCThreads 设置并发标记的线程数 通常为ParallelGCTreads的1/4
- -XX:InitiatingHeapOccupancyPercent 设置触发并发GC周期的Java堆占用阈值
G1通过了三种垃圾回收模式:YoungGC、MixGC和Full GC,在不同的条件下被触发
G1垃圾回收器还提供了一种新的内存区域,叫做Humongous内存区域,主要用于存储大对象,如果超过1.5个region则放入H
如果一个H区装不下一个大对象,那么G1会寻找来连续的H区存储,有时候为了寻找连续的H区,不得不启动Full GC
G1回收垃圾的三个主要环节
- 年轻代GC(Young GC)
- 老年代并发标记过程(Concurrent Marking)
- 混合回收(Mixed GC)
young GC -> young GC + Concurrent Mark -> Mixed GC
当年轻代的Eden区用尽时开始年轻代回收,G1年轻代回收阶段是一个并行的独占式回收器,暂停所有的应用程序线程,启动多线程执行年轻代回收,然后将存活对象从移动到Survivor区间和老年区
当堆空间的内存使用达到一定值(默认为45%),开始老年代并发标记过程
标记完成马上开始混合回收,G1 GC从老年代移动存活对象到空闲区域,这些空闲区域也就成为老年代的一部分,G1的老年代回收不需要整个老年代被回收,只回收一部分老年代Region
一个对象可能被不同的区域所引用,可能出现老年代引用新生代对象的情况
在JVM当中都是使用Remember Set来避免全局扫描
每一个Region都有一个Remember Set,每次进行Reference类型数据写入的时候都会产生一个Write Barrier暂停中断操作,检查将要写入的引用对象是否和该Reference数据类型在不同的Region,如果不同,通过GardTable把相关引用信息记录到引用指向对象的Region的Remember Set,在进行垃圾扫描时,将Remember Set加入GC Roots
也就是说,写入一个对象时,检查该对象的引用是否和该对象在同一个Region,如果不再则在引用指向对象的Remember Set记录
年轻代GC
G1先触发STW,G1创建回收集,回收集是指需要被回收的内存分段集合
将需要回收的Eden和Survivor中存活的对象复制得一个空白区域中变成Survivor,将到达阈值的Survivor复制的一个空白区域当中变成老年代
并发标记过程
- 初始标记阶段:标记从根节点直接可达的对象,这个过程是SWT的,并触发一次年轻代GC
- 根区域扫描:扫描Survivor区直接可达的老年代区域,并标记被引用的对象,这一过程因为要使用Survivor,所以要在年轻代GC之前完成
- 并发标记:在整个堆中进行并发标记,若是发现Region全是可回收对象,这个区域立即回收,同时会计算每个区域对象存活的比例,这个过程可能会被年轻代GC打断
- 再次标记:对并发标记的结果进行修正,STW
- 独占清理:计算各个区域存活对象和GC回收比例,并进行排序,识别可以混合回收的区域,STW
- 并发清理:识别并清理
混合回收
当较多的对象被放入老年区,将会触发混合回收,除了回收这个年轻代,还会回收一部分老年代
总结
垃圾回收器 | 分类 | 作用位置 | 使用算法 | 特点 | 适用场景 |
---|
Serial | 串行 | 新生代 | 复制算法 | 响应速度优先 | 单CPU的client模式 | ParNew | 并行 | 新生代 | 复制算法 | 响应速度优先 | 多CPU的Server模式和CMS配合使用 | Parallel | 并行 | 新生代 | 复制算法 | 吞吐量优先 | 后台运算不需要太多交互 | Serial Old | 串行 | 老年代 | 标记压缩 | 响应速度优先 | 单CPU的client模式 | Parallel Old | 并行 | 老年代 | 标记压缩 | 吞吐量优先 | 后台运算不需要太多交互 | CMS | 并发 | 老年代 | 标记清除 | 响应速度优先 | 互联网或者B/S业务 | G1 | 并发,并行 | 所有位置 | 复制算法,标记压缩 | 响应速度优先 | 面向与服务端 |
|