JVM系列(四)、垃圾收集器


1、垃圾收集器

如果说收集算法时内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。

2、 Serial收集器

串行收集器是最古老、最稳定以及效率高的收集器,它进行垃圾收集时只使用一个线程去回收且必须暂停其他所有的工作线程,直到它收集结束,在用户不可见的情况下把用户正常工作的线程全部停掉,这样会产生较长的停顿。
新生代、老年代使用串行回收;新生代复制算法、老年代标记-整理;垃圾收集的过程会Stop The World(服务暂停),多用于桌面应用,Client 端的垃圾回收器,桌面应用内存小,进行垃圾回收的时间比较短,只要不频繁发生停顿就可以接受。

2.1 参数控制

参数 说明
-XX:+UseSerialGC 串行收集器

Serial收集器示意图

3、 ParNew收集器

3.1 介绍

ParNew收集器其实就是Serial收集器的多线程版本,除了使用多条线程进行垃圾收集之外,其余行为包括Serial收集器可用的所有控制参数(例如:-XX:SurvivorRatio、-XX:PretenureSize’Threshold、-XX:HandlePromotionFailure等)、收集算法、Stop the World 、对象分配规则、回收策略等都与Serial收集器完全一样,在实现上,这两种收集器也共用了相当多的代码。

3.2 特点

  • ParNew 收集器除了多线程收集之外,其他与Serial收集器相比并没有太多创新之处,但它却是许多运行在Server模式下的虚拟机中首选的新生代收集器,其中有一个与性能无关但很重要的原因就是除了Serial收集器之外,目前只有ParNew能与CMS收集器配合工作。
  • 多线程操作存在上下文切换问题,所以建议将 -XX:ParallelGCThreads设置成和CPU核数相同,如果设置太多的话就会产生上下文切换消耗。
  • 新生代并行,老年代串行;新生代复制算法、老年代标记-压缩

并发与并行的概念讲解

  • 并行(Parallel):指多条垃圾收集器线程并行工作,但此时用户线程仍然处于等待状态。
  • 并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),用户程序在继续运行,而垃圾收集程序运行在另一个CPU上。

3.3 参数控制:

参数 说明
-XX:+UseParNewGC ParNew收集器
-XX:ParallelGCThreads 限制线程数量,对应CPU核数

ParNew收集器

4、 Parallel Scavenge 收集器

4.1 介绍

  • Parallel Scavenge 收集器是一个新生代收垃圾集器,也是一个使用复制算法的收集器,与ParNew收集器类似也是多线程收集器。
  • 由于与吞吐量关系密切,Parallel Scavenge 收集器更关注系统的吞吐量,也经常被称为”吞吐量优先”收集器。

所谓的吞吐量就是CPU用于运行用户代码的时间与CPU总消耗的比值,即 吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间),虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那么吞吐量就是 99% ,停顿时间越短越适合需要进行用户交互的程序,良好的相应速度能提升用户体验,而高吞吐量则可以高效地利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。

4.2 特点

  • Parallel Scavenge 收集器的特点是它的关注点与其他收集器不同,CMS等收集器的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge 收集器的目标则是打到可控制的吞吐(Throughput)。
  • 虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供合适的停顿时间或者最大的吞吐量,这种方式称为GC自适应调节策略。
  • 新生代复制算法、老年代标记-压缩算法。

4.3 参数控制:

参数 说明
-XX:+UseParallelGC 使用Parallel Scavenge 收集器+ 老年代串行
-XX:+MaxGCPauseMillis GC 停顿时间,参数配置太小会造成频繁GC
-XX:+GCTimeRatio 吞吐量的值,默认99%

5、 Parallel Old 收集器

Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-压缩”算法。这个收集器是在 JDK1.6中才开始提供。

参数控制:

参数 说明
-XX:+UseParallelOldGC 使用Parallel收集器+ 老年代并行

6、 Serial Old 收集器

它是一个单线程的收集器,使用”标记-整理”算法。

7、 CMS 收集器

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。目前很大一部分的Java应用都几种在互联网站或B/S系统的服务端上,这类应用尤其重视服务的相应速度,希望系统停顿时间最短,以给用户带来较好的体验。

从名字(包含“Mark Sweep”)上就可以看出CMS收集器是基于“标记-清除”算法实现的,他的运作过程相对前几种收集器来说更加复杂一些。整个过程分为四个步骤,包括:

  1. 初始标记(CMS initial Mmark):标记一下GC Roots能够直接关联到的对象,速度很快

  2. 并发标记(CMS concurrent mark):进行GC RootsTracing 的过程

  3. 重新标记(CMS remark):为了修正并发标记期间因用户程序导致标记产生变动的标记记录

  4. 并发清除(CMS concurrent sweep)

CMS 收集器

其中初始标记、重新标记这两个步骤仍需要“Stop The World”。初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快,并发标记阶段就是进行GC Roots Tracing的过程,而重新标记阶段是为了修正并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记时间短。

由于整个过程中耗时最长的并发标记和并发清除过程中,收集器线程都可以与用户线程一起工作,所以总体上来说,CMS收集器的内存回收过程是与用户线程一起并发地执行。老年代收集器(新生代使用ParNew)。

  • 优点: 并发收集、低停顿

  • 缺点: 对CPU 资源非常敏感、无法处理浮动垃圾、产生大量空间碎片、并发阶段会降低吞吐量

参数控制:

参数 说明
-XX:+UseConcMarkSweepGC 使用CMS收集器
-XX:+ UseCMSCompactAtFullCollection Full GC后,进行一次碎片整理;
整理过程是独占的,会引起停顿时间变长
-XX:+CMSFullGCsBeforeCompaction 设置进行几次Full GC后,进行一次碎片整理
-XX:ParallelCMSThreads 设定CMS的线程数量(一般情况约等于可用CPU数量)

8、 G1 收集器

8.1 介绍

G1 收集器是一款面向服务端应用的垃圾收集器。

8.2 特点

上面提到的垃圾收集器,收集的范围都是整个新生代或者老年代,而G1不再是这样。使用G1收集器时,Java堆的内存分布与其他收集器有很大差别,它将整个 Java 堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔阂来,他们都是一部分(可以不连续)Region 的集合。

G1 中每个 Region 都有一个与之对应的 Remembered Set,当进行内存回收时,在 GC 根节点的枚举范围中假如 Remembered Set 即可保证不对全堆扫描也不会有遗漏,检查 Reference 引用的对象是否处于不同的Region

8.3 收集步骤

G1收集器的运作大致可划分为以下几个步骤:

G1 收集器

  1. 初始标记(Initial-Mark):标记一下 GC Roots 能直接关联到的对象,标记这个阶段是停顿的(Stop the Workd Event),并且会触发一次普通的 Mintor GC。对应 GC log :GC pause(young)(initial-mark)
  2. 并发标记(Concurrent Marking):从GC Root 开始对堆中对象进行可达性分析,找出存活的对象,这阶段耗时较长,但可与用户程序并发执行。
  3. 最终标记(Final Marking):为了修正在并发标记期间因用户程序继续运作而导致标记产生边栋的那一部分标记记录。虚拟机将这段时间对象变化记录在线程Remembered Set Logs 里面,最终标记阶段需要把Remembered Set Logs的数据合并到Remembered Set中。
  4. 筛选回收(Live Data Counting and Evacuation)

8.4 G1 收集器的优势

  1. 空间整合:基于”标记-压缩” 算法,实现为主和Region之间采用复制算法实现的垃圾收集
  2. 可预测停顿:这个是G1 与CMS的一大优势,降低停顿时间是G1与CMS共同的关注点,但G1除了追求低停顿之外,还可以预测停顿时间模型
  3. 在G1之前的其他收集器进行收集的范围都是整个新生代或老年代,而G1不再是这样。使用G1收集器时,Java堆的内存布局就与其他收集器有很大差别,它将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留新生代和老年代的概念,但是新生代和老年代不再是物理隔离,他们都是一部分Region(不需要连续)的集合。
  4. G1 收集器之所以能建立可预测的停顿时间模型,是因为它可以有计划地避免在整个Java堆中进行全区域的垃圾收集。G1 跟踪各个Regions 里面的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需时间代价),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region(这也就是Garbage-Fristi名称的来由)。这种使用Region划分内存空间以及有优先级的区域回收方式,保证了G1收集器在有限时间内可以获取尽可能高。

9、常用的收集器组合

新生代
GC策略
老年代
GC策略
说明
组合1 Serial Serial Old Serial和Serial Old都是单线程进行GC,特点就是GC时暂停所有应用线程。
组合2 Serial CMS+Serial Old CMS(Concurrent Mark Sweep)是并发GC,实现GC线程和应用线程并发工作,不需要暂停所有应用线程。另外,当CMS进行GC失败时,会自动使用Serial Old策略进行GC。
组合3 ParNew CMS 使用-XX:+UseParNewGC选项来开启。
ParNew是Serial的并行版本,可以指定GC线程数,默认GC线程数为CPU的数量。
可以使用-XX:ParallelGCThreads选项指定GC的线程数。
如果指定了-XX:+UseConcMarkSweepGC选项,则新生代默认使用ParNew GC策略。
组合4 ParNew Serial Old 使用-XX:+UseParNewGC选项来开启。新生代使用ParNew GC策略,年老代默认使用Serial Old GC策略。
组合5 Parallel Scavenge Serial Old Parallel Scavenge策略主要是关注一个可控的吞吐量:
应用程序运行时间 / (应用程序运行时间 + GC时间),
可见这会使得CPU的利用率尽可能的高,适用于后台持久运行的应用程序,而不适用于交互较多的应用程序。
组合6 Parallel Scavenge Parallel Old Parallel Old是Serial Old的并行版本
组合7 G1GC G1GC -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC #开启;
-XX:MaxGCPauseMillis =50 #暂停时间目标;
-XX:GCPauseIntervalMillis =200 #暂停间隔目标;
-XX:+G1YoungGenSize=512m #年轻代大小;
-XX:SurvivorRatio=6 #幸存区比例

文章作者: hnbian
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hnbian !
评论
 上一篇
JVM系列(五)、常用命令(上) JVM系列(五)、常用命令(上)
一、介绍运用Jvm自带的命令可以方便的和监控和打印堆栈的日志信息帮忙我们来定位问题。虽然Jvm调用成熟的工具已经很多:jconsole、大名鼎鼎的VisualVM、IMBM的Memory Analyzer 等等。但是在生产环境出现问题的时候
2020-01-29
下一篇 
JVM系列(三)、GC算法 JVM系列(三)、GC算法
1. 介绍垃圾收集器( Garbage Collection ) 通常被称为 “GC”,它诞生于1960年 MIT 的 Lisp 语言,经过半个多世纪,目前已经十分成熟了。Jvm中,程序计数器、虚拟机栈、本地方法栈都是随线程而生随线程而灭,
2020-01-10
  目录