浅析经典JVM垃圾收集器-Serial/ParNew/Parallel Scavenge/Serial Old/Parallel Old/CMS/G1(上)

简介: 在讲述垃圾收集器之前,我们得先知道JVM中常见的垃圾收集算法有什么,具体请参考我的这篇博文。如果说收集算法是内存回收的方法论, 那垃圾收集器就是内存回收的实践者。下面就来详细概述下Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS、G1这七款垃圾收集器的特点及使用场景。

基本概念


不同的 GC 各自代表的含义

Partial GC:表示并不收集整个GC堆。

  • Young GC:只收集年轻代的GC。
  • Old GC:只收集老年代的GC。
  • Mixed GC:收集整个年轻代以及部分老年代的GC。这个是 G1 收集器特有的。Full GC:收集整个堆,包括年轻代、老年代、永久代(如果存在的话)等所有部分的GC。


吞吐量(Throughput)

吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量 = 运行用户代码时间 /(运行用户代码时间 + 垃圾收集时间)

假设虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%。

通常来说,停顿时间越短(低延迟)就越适合需要与用户交互或需要保证服务响应质量的程序, 良好的响应速度能提升用户体验;

而高吞吐量则可以最高效率地利用处理器资源,尽快完成程序的运算任务, 主要适合在后台运算而不需要太多交互的分析任务。


垃圾收集器中“并发”和“并行”的概念

并行和并发都是并发编程中的专业名词,在谈论垃圾收集器的上下文语境中, 它们可以理解为:

  • 并行(Parallel):并行描述的是多条垃圾收集器线程之间的关系,说明同一时间有多条这样的线程在协同工作,通常默认此时用户线程是处于等待状态。
  • 并发(Concurrent):并发描述的是垃圾收集器线程与用户线程之间的关系,说明同一时间垃圾收集器线程与用户线程都在运行。 由于用户线程并未被冻结,所以程序仍然能响应服务请求, 但由于垃圾收集器线程占用了一部分系统资源, 此时应用程序的处理的吞吐量将受到一定影响。


垃圾收集器


Serial 收集器

特点:

  • 客户端模式下的默认新生代收集器
  • 单线程工作(它的“单线程”的意义并不仅仅是说明它只会使用一个处理器或一条收集线程去完成垃圾收集工作,更重要的是强调在它进行垃圾收集时,必须暂停其他所有工作线程,直到它收集结束)
  • 简单而高效(与其他收集器的单线程相比)
  • 对于内存资源受限的环境, 它是所有收集器里额外内存消耗(Memory Footprint)最小的
  • 对于单核处理器或处理器核心数较少的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率

Serial/Serial Old收集器协同工作运行示意图如下:

网络异常,图片无法展示
|


ParNew 收集器

特点:

  • Serial收集器的多线程并行版本(除了同时使用多条线程进行垃圾收集之外, 其余的行为(包括Serial收集器可用的所有控制参数、收集算法、Stop The World、对象分配规则、回收策略等)都与Serial收集器完全一致,在实现上这两种收集器也共用了相当多的代码)
  • JDK 7之前在服务端模式下首选的新生代收集器(一个重要原因:除了Serial收集器外, 目前只有它能与 CMS 收集器配合工作)
  • ParNew收集器是激活CMS后(使用-XX: +UseConcMarkSweepGC选项) 的默认新生代收集器,也可以使用-XX: +/-UseParNewGC选项来强制指定或者禁用它。
  • ParNew收集器在单核心处理器的环境中绝对不会有比Serial收集器更好的效果
  • 默认开启的收集线程数与处理器核心数量相同(可以使用-XX: ParallelGCThreads参数来限制垃圾收集的线程数)

ParNew/Serial Old收集器协同工作运行示意图如下:

网络异常,图片无法展示
|


Parallel Scavenge 收集器

特点:

  • 新生代收集器
  • 基于标记-复制算法实现
  • 多线程收集器
  • 吞吐量优先收集器(Parallel Scavenge收集器其他收集器不同在于:CMS等收集器的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间, 而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量
  • 自适应调节策略(通过+UseAdaptiveSizePolicy参数激活)


参数:

  • -XX: MaxGCPauseMillis参数:允许的值是一个大于0的毫秒数, 收集器将尽力保证内存回收花费的时间不超过用户设定值。
  • -XX: GCTimeRatio参数:一个大于0小于100的整数, 也就是垃圾收集时间占总时间的比率, 相当于吞吐量的倒数。 譬如把此参数设置为19, 那允许的最大垃圾收集时间就占总时间的5%(即1/(1+19)) , 默认值为99, 即允许最大1%(即1/(1+99)) 的垃圾收集时间。
  • +UseAdaptiveSizePolicy参数:这是一个开关参数, 当这个参数被激活之后, 就不需要人工指定新生代的大小(-Xmn) 、 Eden与Survivor区的比例(-XX: SurvivorRatio) 、 晋升老年代对象大小(-XX:PretenureSizeThreshold) 等细节参数了, 虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量。只需要把基本的内存数据设置好(如-Xmx设置最大堆),然后使用-XX: MaxGCPauseMillis参数(更关注最大停顿时间)或-XX: GCTimeRatio(更关注吞吐量) 参数给虚拟机设立一个优化目标, 那具体细节参数的调节工作就由虚拟机完成了。


Serial Old 收集器


特点:

  • Serial收集器的老年代版本
  • 单线程收集器
  • 使用标记-整理算法
  • 主要在客户端模式下使用。如果在服务端模式下,它也可能有两种用途: 一种是在JDK 5以及之前的版本中与Parallel Scavenge收集器搭配使用,另外一种就是作为CMS收集器发生失败时的后备预案,在并发收集发生Concurrent Mode Failure时使用

Serial/Serial Old收集器协同工作运行示意图如下:

网络异常,图片无法展示
|


Parallel Old 收集器(JDK 6中发布)

特点:

  • Parallel Scavenge收集器的老年代版本
  • 多线程并发收集
  • 使用标记-整理算法
  • 在注重吞吐量或者处理器资源较为稀缺的场合, 都可以优先考虑Parallel Scavenge加Parallel Old收集器这个组合。

Parallel Scavenge/Parallel Old 收集器运行示意图如下:

网络异常,图片无法展示
|


CMS 收集器(JDK 5中发布)

特点:

  • 老年代的收集器
  • 使用标记-清除算法
  • 第一款真正意义上支持并发的垃圾收集器,首次实现了让垃圾收集线程与用户线程(基本上)同时工作(并发收集)
  • 以获取最短回收停顿时间为目标的收集器(低停顿)
  • 常用于互联网网站或者基于浏览器的B/S系统的服务端(这类应用通常都会较为关注服务的响应速度,希望系统停顿时间尽可能短,以给用户带来良好的交互体验)

收集流程:

CMS整个过程的四个步骤如下,其中初始标记、 重新标记这两个步骤仍然需要“Stop The World”。

  1. 初始标记-仅仅只是标记一下GC Roots能直接关联到的对象,速度很快
  2. 并发标记-从GC Roots的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行
  3. 重新标记-为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间通常会比初始标记阶段稍长一些, 但也远比并发标记阶段的时间短
  4. 并发清除-清理删除掉标记阶段判断的已经死亡的对象, 由于不需要移动存活对象, 所以这个阶段也是可以与用户线程同时并发的

Concurrent Mark Sweep 收集器运行示意图如下:

网络异常,图片无法展示
|


缺点:

  • CMS 收集器对处理器资源非常敏感。事实上,面向并发设计的程序都对处理器资源比较敏感。在并发阶段,它虽然不会导致用户线程停顿,但却会因为占用了一部分线程而导致应用程序变慢,降低总吞吐量。
  • 由于CMS收集器无法处理“浮动垃圾”, 有可能出现“Con-current Mode Failure”失败进而导致另一次完全“Stop The World”的Full GC的产生。
  • CMS是一款基于“标记-清除”算法实现的收集器,这意味着收集结束时会有大量空间碎片产生,空间碎片过多时,将会给大对象分配带来很大麻烦,往往会出现老年代还有很多剩余空间, 但就是无法找到足够大的连续空间来分配当前对象, 而不得不提前触发一次Full GC的情况。

关于CMS收集器浮动垃圾的说明:

由于在CMS的并发标记和并发清理阶段,用户线程是还在继续运行的,程序在运行自然就还会伴随有新的垃圾对象不断产生,但这一部分垃圾对象是出现在标记过程结束以后,CMS无法在当次收集中处理掉它们, 只好留待下一次垃圾收集时再清理掉。这一部分垃圾就称为“浮动垃圾”。

由于在垃圾收集阶段用户线程还需要持续运行, 那就还需要预留足够内存空间提供给用户线程使用, 因此CMS收集器不能像其他收集器那样等待到老年代几乎完全被填满了再进行收集,必须预留一部分空间供并发收集时的程序运作使用


G1 收集器(Garbage First)

特点:

  • 将堆内存“化整为零”,开创了收集器面向局部收集的设计思路和基于Region的内存布局形式
  • 面向全堆的收集器,不再需要其他新生代收集器的配合工作
  • 主要面向服务端应用的垃圾收集器
  • G1 仍是遵循分代收集理论设计的, 但其堆内存的布局与其他收集器有非常明显的差异
  • G1 不再坚持固定大小以及固定数量的分代区域划分,而是把连续的Java堆划分为多个大小相等的独立Region,每一个Region都可以根据需要,扮演新生代的Eden空间、Survivor空间或者老年代空间
  • 收集器能够对扮演不同角色的Region采用不同的策略去处理, 这样无论是新创建的对象还是已经存活了一段时间、熬过多次收集的旧对象都能获取很好的收集效果
  • Region中还有一类特殊的Humongous区域, 专门用来存储大对象。 G1认为只要大小超过了一个Region容量一半的对象即可判定为大对象。 每个Region的大小可以通过参数-XX: G1HeapRegionSize设定, 取值范围为1MB~32MB, 且应为2的N次幂。 而对于那些超过了整个Region容量的超级大对象,将会被存放在N个连续的Humongous Region之中, G1的大多数行为都把Humongous Region作为老年代的一部分来进行看待
  • G1收集器之所以能建立可预测的停顿时间模型, 是因为它将Region作为单次回收的最小单元, 即每次收集到的内存空间都是Region大小的整数倍, 这样可以有计划地避免在整个Java堆中进行全区域的垃圾收集
  • G1收集器去跟踪各个Region里面的垃圾堆积的“价值”大小, 价值即回收所获得的空间大小以及回收所需时间的经验值, 然后在后台维护一个优先级列表, 每次根据用户设定允许的收集停顿时间(使用参数-XX: MaxGCPauseMillis指定, 默认值是200毫秒),优先处理回收价值收益最大的那些Region
  • 可以由用户指定期望的停顿时间是G1收集器很强大的一个功能,设置不同的期望停顿时间, 可使得G1在不同应用场景中取得关注吞吐量和关注延迟之间的最佳平衡(默认的停顿目标为两百毫秒,通常把期望停顿时间设置为一两百毫秒或者两三百毫秒会是比较合理的)


G1 收集器 Region 分区示意图如下:

网络异常,图片无法展示
|


相关文章
|
6月前
|
存储 算法 Oracle
极致八股文之JVM垃圾回收器G1&ZGC详解
本文作者分享了一些垃圾回收器的执行过程,希望给大家参考。
|
3月前
|
存储 算法 Java
JVM进阶调优系列(10)敢向stop the world喊卡的G1垃圾回收器 | 有必要讲透
本文详细介绍了G1垃圾回收器的背景、核心原理及其回收过程。G1,即Garbage First,旨在通过将堆内存划分为多个Region来实现低延时的垃圾回收,每个Region可以根据其垃圾回收的价值被优先回收。文章还探讨了G1的Young GC、Mixed GC以及Full GC的具体流程,并列出了G1回收器的核心参数配置,帮助读者更好地理解和优化G1的使用。
|
4月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
174 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
4月前
|
存储 监控 算法
JVM调优深度剖析:内存模型、垃圾收集、工具与实战
【10月更文挑战第9天】在Java开发领域,Java虚拟机(JVM)的性能调优是构建高性能、高并发系统不可或缺的一部分。作为一名资深架构师,深入理解JVM的内存模型、垃圾收集机制、调优工具及其实现原理,对于提升系统的整体性能和稳定性至关重要。本文将深入探讨这些内容,并提供针对单机几十万并发系统的JVM调优策略和Java代码示例。
84 2
|
4月前
|
Java
JVM进阶调优系列(5)CMS回收器通俗演义一文讲透FullGC
本文介绍了JVM中CMS垃圾回收器对Full GC的优化,包括Stop the world的影响、Full GC触发条件、GC过程的四个阶段(初始标记、并发标记、重新标记、并发清理)及并发清理期间的Concurrent mode failure处理,并简述了GC roots的概念及其在GC中的作用。
|
6月前
|
存储 算法 Java
JVM自动内存管理之垃圾收集算法
文章概述了JVM内存管理和垃圾收集的基本概念,提供一个关于JVM内存管理和垃圾收集的基础理解框架。
JVM自动内存管理之垃圾收集算法
|
6月前
|
存储 算法 Java
JVM组成结构详解:类加载、运行时数据区、执行引擎与垃圾收集器的协同工作
【8月更文挑战第25天】Java虚拟机(JVM)是Java平台的核心,它使Java程序能在任何支持JVM的平台上运行。JVM包含复杂的结构,如类加载子系统、运行时数据区、执行引擎、本地库接口和垃圾收集器。例如,当运行含有第三方库的程序时,类加载子系统会加载必要的.class文件;运行时数据区管理程序数据,如对象实例存储在堆中;执行引擎执行字节码;本地库接口允许Java调用本地应用程序;垃圾收集器则负责清理不再使用的对象,防止内存泄漏。这些组件协同工作,确保了Java程序的高效运行。
54 3
|
7月前
|
存储 算法 安全
(八)JVM成神路之GC分区篇:G1、ZGC、ShenandoahGC高性能收集器深入剖析
在《GC分代篇》中,我们曾对JVM中的分代GC收集器进行了全面阐述,而在本章中重点则是对JDK后续新版本中研发推出的高性能收集器进行深入剖析。
252 12
|
7月前
|
算法 安全 Java
(七)JVM成神路之GC分代篇:分代GC器、CMS收集器及YoungGC、FullGC日志剖析
在《GC基础篇》中曾谈到过分代以及分区回收的概念,但基础篇更多的是建立在GC的一些算法理论上进行高谈阔论,而本篇则重点会对于分代收集器的实现进行全面详解,其中会涵盖串行收集器、并行收集器、三色标记、SATB算法、GC执行过程、并发标记、CMS收集器等知识,本篇则偏重于分析GC机制的落地实现,也就是垃圾收集器(Garbage Collector)。
206 8
|
6月前
|
C# UED 开发者
WPF打印功能实现秘籍:从页面到纸张,带你玩转WPF打印技术大揭秘!
【8月更文挑战第31天】在WPF应用开发中,打印功能至关重要,不仅能提升用户体验,还增强了应用的实用性。本文介绍WPF打印的基础概念与实现方法,涵盖页面元素打印、打印机设置及打印预览。通过具体案例,展示了如何利用`PrintDialog`和`PrintDocument`控件添加打印支持,并使用`PrinterSettings`类进行配置,最后通过`PrintPreviewWindow`实现打印预览功能。
626 0