一.前言

前文学习了各种GC回收算法,掌握了GC回收的原理,但是真正的GC实现却尤为复杂,本篇文章将主要介绍各种GC收集器。

目前主流的HotSpot VM支持多种虚拟机,这些虚拟机也体现了GC的发展历程,从单线程GC到多线程GC,分代GC到G1 GC。

本文主要从以下几个方面介绍GC收集器:

  • 各种GC的特点
  • GC匹配和参数使用
  • GC日志格式
  • 常用的GC参数总结

### 二.各种GC的特点

HotSpot中采用分代GC,从早期的单线程串行Garbage Collector到后面的多线程并行Garbage Collecot,衍生出了很多款Collector。

其中负责收集年轻代:

  1. Serial:单线程串行收集器,使用复制算法回收年轻代
  2. ParNew:多线程并行收集器,使用复制算法回收年轻代
  3. Parallel Scavenge:多线程并行收集器,使用复制算法回收年轻代

其中负责收集老年代:

  1. Serial Old:类似Serial,单线程串行收集器,使用标记整理算法回收老年代
  2. CMS:并发标记整理的收集器,使用标记清除算法回收老年代
  3. Parallel Old:类似ParNew和Parallel Scavenge,使用标记整理算法回收老年代

1.Serial收集器

Serial收集器是一款非常古老的收集器,它使用单线程串行方式回收年轻代,会产生STW。

Note:

STW即Stop The World,即停止所有用户线程,只有GC线程在运行。

每次进行GC时,首先停顿所有的用户线程,然后只有一个GC线程回收年轻代中的死亡对象。在Java Client模式中,默认任然使用Serial,因为Client模式主要针对桌面应用,一般内存较小,在百M范围内,使用单线程收集Serial效率非常高,可以带来很少时间的停顿,用户体检非常好。

2.ParNew收集器

在早期,只有单线程收集器时,年轻代别无选择。后续又演变成多线程GC年轻代,便衍生出ParNew这款并行收集器,它的并行实现主要是在GC期间使用多线程回收年轻代。

这款并行收集器在GC期间,也需要STW。一般多数用于Server端的年轻代GC。

3.Parallel Scavenge收集器

顾名思义,这个款年轻代收集器也是并行收集器,和ParNew的功能差不多,同样适用复制算法。但是它更注重系统运行的吞吐量。这里说的吞吐量,指的是CPU用于运行应用程序的时间和CPU总时间的占比,吞吐量 = 代码运行时间 / (代码运行时间 + 垃圾收集时间)。但是它的来源比较奇葩,没有遵循GC框架,导致和CMS不能兼容。关于这点可以参考:

ParNew 和 PSYoungGen 和 DefNew 是一个东西么?

4.Serial Old收集器

该收集器和Serial收集器的功能一样,都是单线程串行收集器,GC期间也会STW。但是它用于收集老年代且使用了标记整理算法,这两点它和Serial收集器不一样。主要也是应用在Client模式下的桌面应用中。

5.Parallel Old收集器

Parallel Old和ParNew和Parallel Scavenge类似,是一款老年代的多线程并行收集器。一般只配合Parallel Scavenge使用。

6.CMS收集器

CMS(Concurrent Mark Sweep 并发标记清理)收集器是日常应用中最常被使用的收集器。它主要是为了减少停顿的时间,降低延迟而生,多应用对实时性要求比较的应用场景,如:互联网应用。

它主要分为四个过程:

  1. 初始标记:这阶段将标记不可达对象,标记阶段将暂停所有用户线程,造成STW。但是这阶段只是标记出GC Roots,停顿时间相对较短。
  2. 并发标记:这阶段GC线程将会和用户线程同时运行,将从初始阶段标记出的GC Roots出发标记老年代所有对象
  3. 重新标记:这阶段将暂停所用用户线程,造成STW。但是同样相对较短,主要是为了重新标记出在并发阶段发生引用变化的对象,因为并发标记阶段是和用户线程并发运行,可能会造成对象的引用关系发生变化。
  4. 并发清除:这是最后一个阶段,也是和用户线程同时运行的。将并发的清理掉被标记的死亡对象。

其中初始标记和重新标记仍然会STW暂停用户线程,但是这两个过程的停顿时间相对于并发标记和并发清除而言相对较短,而并发标记和并发清除阶段GC线程则可以和用户线程并发运行。

由于CMS收集器同样使用标记清除算法,所以存在内存碎片问题,从而可能造成大对象无法分配发生提前GC。所以CMS收集器又提供了参数控制其进行内存碎片整理,默认是开启状态,这个过程是非常长的。

三.GC匹配和参数使用

从以上内容介绍,可以看出分代GC分为很多种,随着演化过程,每种都有各自的应用场景。从其收集特点上可以分为三类:

  1. 单线程串行收集
  2. 多线程并发串行收集
  3. 多阶段并行收集

虽然这些分代收集器种类繁多,但是他们之间有相互匹配,并非任意使用。配对的使用情况以及参数见下表:

young old 参数
Serial Serial old -XX:+UseSerialGC
ParNew Serial old -XX:+UseParNewGC
Parallel Scavenge Serial old -XX:+UseParallelGC
Parallel Scavenge Parallel Old -XX:+UseParallelOldGC
ParNew CMS + Serial Old -XX:+UseConcMarkSweepGC
Serial CMS -XX:+UseConcMarkSweepGC -XX:-UseParNewGC

### 四.GC日志格式

GC收集器众多,每种GC收集器的GC日志格式都不一样。这里笔者做了下总结,同样是针对以上的各种匹配情况做了日志格式的收集。

1.Serial + Serial old格式

0.299: [GC (Allocation Failure) 0.299: [DefNew: 1770K->428K(4928K), 0.0019955 secs] 9962K->8620K(15872K), 0.0020405 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.304: [GC (Allocation Failure) 0.304: [DefNew (promotion failed) : 4612K->4193K(4928K), 0.0014249 secs]0.305: [Tenured: 8588K->4499K(10944K), 0.0034912 secs] 12804K->4499K(15872K), [Metaspace: 3094K->3094K(1056768K)], 0.0049774 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]

使用Serial + Serial Old搭配式,年轻代收集将会是DefNew,老年代将会是Tenured。

1. ParNew + Serial old格式

0.243: [GC (Allocation Failure) 0.243: [ParNew: 1770K->470K(4928K), 0.0029402 secs] 9962K->8662K(15872K), 0.0029933 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
0.251: [GC (Allocation Failure) 0.251: [ParNew (promotion failed): 4566K->4096K(4928K), 0.0009985 secs]0.252: [Tenured: 8632K->4516K(10944K), 0.0025514 secs] 12758K->4516K(15872K), [Metaspace: 3093K->3093K(1056768K)], 0.0035906 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

使用Serial + Serial Old搭配式,年轻代收集将会是ParNew,老年代将会是Tenured。

3.Parallel Scavenge + Parallel Old格式

0.224: [GC (Allocation Failure) [PSYoungGen: 0K->0K(4608K)] 8619K->8619K(15872K), 0.0004876 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.225: [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(4608K)] [ParOldGen: 8619K->8601K(11264K)] 8619K->8601K(15872K), [Metaspace: 3080K->3080K(1056768K)], 0.0046498 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]

使用Parallel Scavenge + Parallel Old搭配式,年轻代收集将会是PSYoungGen,老年代将会是ParOldGen。

4.ParNew + CMS格式

0.351: [GC (Allocation Failure) 0.351: [ParNew: 1769K->446K(4928K), 0.0008713 secs] 9961K->8638K(15872K), 0.0009216 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
0.354: [GC (CMS Initial Mark) [1 CMS-initial-mark: 8192K(10944K)] 12734K(15872K), 0.0004513 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.355: [CMS-concurrent-mark-start]
0.355: [CMS-concurrent-mark: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.355: [GC (Allocation Failure) 0.355: [ParNew (promotion failed): 4631K->4267K(4928K), 0.0007242 secs]0.356: [CMS (concurrent mode failure): 8207K->4500K(10944K), 0.0031171 secs] 12823K->4500K(15872K), [Metaspace: 3080K->3080K(1056768K)], 0.0038915 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

使用CMS收集器的GC日志格式非常明显,有CMS的GC过程,年轻代将使用ParNew标志。

### 五.常用的GC参数总结

1.开启GC日志

  • -verbose:gc:打印GC日志
  • -XX:+PrintGCDateStamps:打印GC日志时间戳
  • -XX:PrintGCDetails:打印GC日志详情
  • -XX:+PrintGCTimeStamps:打印此次GC距离JVM开始运行的时间
  • -XX:+PrintGCApplicationStopedTime:打印GC造成的应用暂停时间
  • -XX:+PrintTenuringDistribution:打印对象晋升日志

2.通用参数

  • -XX:+HeapDumpOnOutOfMemoryError:内存溢出时,产生heap dump文件
  • -Xloggc::将GC日志输出到指定文件
  • -XX:-+DisableExplicitGC:禁用System.gc(),该方法默认会触发FGC
  • -XX:MaxTenuringThreshold: 新生代 to 区的对象在经过多次 GC 后,如果还没有死亡,则认为他是一个老对象,则可以晋升到老年代,默认是15。但该参数不是唯一决定对象晋升的条件,当 to区不够或者该对象年龄已经达到了平均晋升值或者大对象等等条件
  • -XX:TargetSurvivorRatio 决定对何时晋升的不仅只有 XX:MaxTenuringThreshold 参数,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半(默认50%)
  • -XX:+UseTLAB:启用线程本地分配缓存,默认开启
  • -XX:+PrintTLAB:打印TLAB的使用情况
参考

Our Collectors

ParNew 和 PSYoungGen 和 DefNew 是一个东西么?

JVM GC系列 — GC收集器的更多相关文章

  1. 深入JVM系列(二)之GC机制、收集器与GC调优

    一.回想JVM内存分配 须要了解很多其它内存模式与内存分配的,请看 深入JVM系列(一)之内存模型与内存分配 1.1.内存分配: 1.对象优先在EDEN分配 2.大对象直接进入老年代  3.长期存活的 ...

  2. 深入JVM系列(二)之GC机制、收集器与GC调优(转)

    一.回顾JVM内存分配   需要了解更多内存模式与内存分配的,请看 深入JVM系列(一)之内存模型与内存分配 1.1.内存分配: 1.对象优先在EDEN分配2.大对象直接进入老年代 3.长期存活的对象 ...

  3. JVM(2)——GC算法和收集器

    一.引入 上篇博客<JVM--简介>中主要介绍了JVM的内存模型,思考一下: 为什么要划分堆.栈.方法区等? 为什么把不同种类的数据信息分别存放? 答案可以分为很多很多条,这里就说一个方面 ...

  4. JVM探秘4---垃圾收集器介绍

    Java虚拟机有很多垃圾收集器 下面先来了解HotSpot虚拟机中的7种垃圾收集器:Serial.ParNew.Parallel Scavenge.Serial Old.Parallel Old.CM ...

  5. JVM之Parallel Scavenge收集器

    新生代收集器,复制算法,并行收集,面向吞吐量要求(吞吐量优先收集器). 吞吐量=用户代码运行时间/(用户代码运行时间+垃圾回收时间) -XX:MaxGCPauseMillis:控制最大垃圾收集停顿时间 ...

  6. 深入理解JVM(③)ZGC收集器

    前言 ZGC是一款在JDK11中新加入的具有实验性质的低延迟垃圾收集器,目前仅支持Linux/x86-64.ZGC收集器是一款基于Region内存布局的,(暂时)不设分代的,使用了读屏障.染色指针和内 ...

  7. JVM GC系列 — GC算法

    一.前言 从本篇文章开始,将开始一个新的系列JVM.JVM是一个非常庞大且复制的技术体系,但是对于程序猿的升级,走向更高阶所必要经历的,曾经也下决心要好好学习一番,然而毅力不足都中途放弃. GC的作用 ...

  8. JVM之Parallel Old收集器

    Parallel Scavenge的老年代版本 标记-整理算法 注重吞吐量及cpu资源敏感环境.

  9. 学习JVM--垃圾回收(二)GC收集器

    1. 前言 在上一篇文章中,介绍了JVM中垃圾回收的原理和算法.介绍了通过引用计数和对象可达性分析的算法来筛选出已经没有使用的对象,然后介绍了垃圾收集器中使用的三种收集算法:标记-清除.标记-整理.标 ...

随机推荐

  1. nginx学习(六):日志切割

    现有的日志都会存在 access.log 文件中,但是随着时间的推移,这个文件的内容会越来越多,体积会越来越大,不便于运维人员查看,所以我们可以通过把这个大的日志文件切割为多份不同的小文件作为日志,切 ...

  2. 高阶组件&&高阶函数(一)

    antd里面的form表单方面,遇到一个高阶函数,以及高阶组件,于是看了一下这方面内容,前辈们的文章写得也非常详细,这里就稍微kobe一下 高阶函数与高阶组件 高阶函数: 高阶函数,是一种特别的函数, ...

  3. mysql workbench 报错:Can't analyze file, please try to change encoding type...

    Mysql workbench 导入csv can't analyze file 原因: workbench 识别csv第一行作为column名,column名不能为中文,所以报错.解决方法:csv第 ...

  4. javascript基础修炼(12)——手把手教你造一个简易的require.js

    目录 一. 概述 二. require.js 2.1 基本用法 2.2 细说API设计 三. 造轮子 3.1 模块加载执行的步骤 3.2 代码框架 3.3 关键函数的代码实现 示例代码托管在我的代码仓 ...

  5. SSM框架之spring(1)

    spring(1) 1.spring概述 Spring是分层的 Java SE/EE应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control:反转控制)和 AOP( ...

  6. 关于MySql 数据库InnoDB存储引擎介绍

    熟悉MySQL的人,都知道InnoDB存储引擎,如大家所知,Redo Log是innodb的核心事务日志之一,innodb写入Redo Log后就会提交事务,而非写入到Datafile.之后innod ...

  7. JS 输出

    JS 输出 JavaScript 通常用于操作 HTML 元素. 操作 HTML 元素 如需从 JavaScript 访问某个 HTML 元素,您可以使用 document.getElementByI ...

  8. SQL Server 查询某一个数据库存储过程、函数是否包含某一个内容或者脚本

    SELECT obj.Name 名称, sc.TEXT 内容FROM syscomments scINNER JOIN sysobjects obj ON sc.Id = obj.IDWHERE sc ...

  9. c# 获取sqlserver 运行脚本的print消息的方法分享

    转自:http://www.maomao365.com/?p=6923  摘要: 在sql脚本的编写中,我们经常使用sql脚本print消息,作为输出测试, 通过获取print消息,我们可以快速获取程 ...

  10. JAVA跨域、RestTemplate高并发下异常与配置、JSON数据Long转String

    ## 跨域支持 import org.springframework.context.annotation.Bean; import org.springframework.context.annot ...