JDK垃圾收集器
一、Serial GC垃圾收集器
Serial GC(串行垃圾收集器)是一种单线程的垃圾回收器,主要用于小型应用或单核系统中,设计目标是简单和高效,尤其适合堆内存较小且对吞吐量要求不高的场景。Serial GC 是JVM最早期的垃圾回收器之一,虽然现在大多数场景下都被更先进的GC替代,但它在一些特定场合仍然有其用武之地。
Serial GC的工作原理
Serial GC 的核心特点是所有垃圾回收工作都是单线程串行执行的,并且在回收期间会完全暂停应用程序的所有线程(Stop-the-World)。它分别在年轻代和老年代使用不同的算法进行垃圾回收:
-
年轻代GC:
- 在年轻代,Serial GC使用的是标记-复制算法(Mark-Copy Algorithm)。当新生代的Eden区填满时,GC线程会暂停所有应用线程,然后将存活的对象从Eden区复制到Survivor区或老年代。这个过程是单线程的,并且所有其他应用线程都会暂停,直到GC完成。
-
老年代GC:
- 在老年代,Serial GC采用标记-压缩算法(Mark-Compact Algorithm)。当老年代需要回收时,Serial GC会首先标记所有存活的对象,接着将这些对象压缩到堆的一端,从而释放出连续的内存空间。同样,这个过程也是单线程的,并且需要暂停所有应用线程(Stop-the-World)。
Serial GC的特点
-
单线程执行:
- Serial GC 是单线程垃圾收集器,不会使用多线程并行回收内存。这意味着即使在多核系统上,垃圾回收过程也只会使用一个CPU核心。
-
Stop-the-World:
- 在垃圾回收期间,所有应用线程都会暂停,直到垃圾回收完成。这会导致应用程序在GC过程中出现较长时间的停顿,因此Serial GC并不适用于需要低延迟或实时响应的场景。
-
简单高效:
- 由于Serial GC没有复杂的并发和多线程操作,算法简单,管理开销较小,在小堆内存环境下表现非常高效。
-
适合小型堆内存:
- Serial GC非常适合堆内存较小的应用(通常小于100MB),在这种情况下,其停顿时间不会太长,因此不会显著影响应用的性能。
Serial GC的配置参数
-
启用Serial GC:
-XX:+UseSerialGC
:通过此参数可以在JVM中启用Serial GC。默认情况下,JVM会根据系统配置自动选择GC,但在一些小型应用或嵌入式设备上,Serial GC是默认选择。
-
堆内存设置:
-Xms<size>
和-Xmx<size>
:通过这两个参数可以设置JVM的初始堆内存和最大堆内存大小,这对于Serial GC尤其重要,因为它适合小内存应用。
Serial GC的适用场景
-
小型应用:
- Serial GC适合那些对响应时间要求不高的小型应用,特别是单线程的命令行程序或批处理任务。
-
嵌入式系统:
- 在资源有限的嵌入式系统或老旧的单核硬件设备上,Serial GC由于其简单性和低管理开销,是一个理想的选择。
-
单核或少核系统:
- Serial GC在多核系统上表现不佳,但在单核或少核系统上,由于没有线程切换和并行任务管理,反而能提供相对高效的垃圾回收。
-
测试或开发环境:
- 在开发环境或对性能要求不高的测试环境中,Serial GC的简单实现可能是一个合理的选择,因为它的行为简单、可预测。
Serial GC的优缺点
优点:
- 简单高效: 算法简单,执行效率在小堆内存环境中非常高,没有额外的并行、并发开销。
- 适合单核系统: 在单核系统中,没有并发回收的复杂性,能更高效地回收垃圾。
- 低开销: 管理开销小,适合嵌入式系统或资源受限的环境。
缺点:
- 全堆暂停: Serial GC在回收内存时需要暂停所有应用线程,导致长时间的应用停顿,尤其是堆内存较大时停顿时间更长。
- 无法利用多核: 在现代多核系统上,Serial GC无法充分利用多个CPU核心,因此在大规模应用场景下表现不佳。
- 不适合大内存和高并发应用: 对于需要处理大量数据的应用,Serial GC的停顿时间过长,无法满足性能要求。
Serial GC的总结
Serial GC 是一种简单的垃圾回收器,适合在小型应用和单核系统中使用。它的优势在于算法简单、管理开销小,适合在小堆内存和CPU资源有限的场景下使用。然而,由于它的Stop-the-World机制和无法利用多核的缺陷,它并不适用于现代的大规模、多线程、高并发应用场景。在这种情况下,像Parallel GC、G1 GC或ZGC等更高级的垃圾收集器会是更好的选择。
二、Parallel GC垃圾收集器
Parallel GC(Parallel Garbage Collector),又称为吞吐量优先垃圾收集器,是一种注重高吞吐量的垃圾回收器。它是JVM的默认垃圾回收器之一,特别适合在多核环境下并发执行垃圾回收任务,并且它的设计目标是最大限度地提高应用程序的吞吐量,而不是最小化垃圾回收时的停顿时间。
Parallel GC的工作原理
Parallel GC主要依赖并行执行垃圾回收任务,即在进行垃圾回收时,JVM会启动多个线程并发地执行垃圾收集工作。它的两个核心部分分别是年轻代垃圾回收和老年代垃圾回收:
-
年轻代GC(Young GC):
- Parallel GC对新生代采用标记-复制算法。当新生代空间填满时,Parallel GC会启动多个线程并发地执行新生代垃圾回收任务,将存活的对象复制到Survivor区或直接提升到老年代。这个过程是Stop-the-World(STW)的,会暂停所有应用线程。
-
老年代GC(Old GC):
- 对于老年代,Parallel GC使用标记-压缩算法(Mark-Compact)。当老年代需要进行回收时,Parallel GC会停止应用线程,首先标记出所有存活的对象,然后将存活的对象压缩到堆的一端,释放未使用的内存空间。整个老年代回收过程也是Stop-the-World的。
Parallel GC的特点
-
高吞吐量:
- Parallel GC的主要目标是提高吞吐量,即应用程序用于执行实际任务的时间比例。它通过在年轻代和老年代垃圾回收时使用多线程并行化操作,最大化CPU的利用率。
-
全堆暂停(Stop-the-World):
- 虽然Parallel GC提高了吞吐量,但它的垃圾回收过程是完全暂停应用线程的。在垃圾回收期间,应用程序的所有线程都会停止,直到GC完成。这意味着在响应时间敏感的应用中,可能会导致较长的停顿。
-
适合大堆内存和多核处理器:
- Parallel GC特别适合有大量可用CPU核心的多核服务器环境,因为它可以充分利用多线程并行执行垃圾回收任务。在大堆内存应用中,Parallel GC也能表现出较好的性能。
-
配置简单:
- Parallel GC在默认配置下就能提供良好的吞吐量优化,适合不需要严格GC调优的场景。开发者只需简单地调整堆大小和线程数即可。
Parallel GC的配置参数
常见的Parallel GC的配置参数如下:
-
启用Parallel GC:
-XX:+UseParallelGC
:启用Parallel GC,默认在许多JVM版本中即为启用状态。-XX:+UseParallelOldGC
:启用Parallel GC的老年代并行回收(一般在老JDK版本中需要手动启用)。
-
GC线程数:
-XX:ParallelGCThreads=<N>
:设置GC线程的数量,通常是系统CPU核心数的1到4倍,默认会根据硬件配置自动调整。
-
最大GC暂停时间:
-XX:MaxGCPauseMillis=<N>
:设置目标GC的最大暂停时间。JVM会尝试在这个目标时间内完成GC操作,但不能完全保证。
-
吞吐量目标:
-XX:GCTimeRatio=<N>
:控制GC时间占总时间的比例,默认值是99,表示GC时间占总时间的1%。
-
自适应调整:
-XX:+UseAdaptiveSizePolicy
:启用自适应大小调整策略,JVM会根据运行时状况动态调整年轻代和老年代的大小,优化性能。
Parallel GC的适用场景
Parallel GC特别适合以下场景:
-
高吞吐量需求:
- 如果应用程序对响应时间没有严格要求,但需要尽可能多的时间用于处理业务逻辑(而非GC),Parallel GC是一个理想选择。
-
多核服务器:
- Parallel GC的并行垃圾回收机制能够在多核系统中充分利用CPU资源,特别适合多核服务器或大规模分布式系统。
-
大内存应用:
- 对于堆内存较大的应用,Parallel GC在进行垃圾回收时能更有效地管理大量内存,从而提升系统的整体吞吐量。
Parallel GC的优缺点
优点:
- 高吞吐量: 通过并行执行垃圾回收任务,最大化CPU利用率,适合对吞吐量有高要求的应用。
- 简单易用: Parallel GC是JVM中的默认垃圾回收器,使用它无需复杂的配置,能在大多数情况下提供良好的性能。
缺点:
- 停顿时间较长: Parallel GC在垃圾回收时需要Stop-the-World,应用程序会暂停运行,这对响应时间要求较高的应用可能不适用。
- 不适合低延迟应用: 如果应用对延迟敏感,Parallel GC可能会导致不可预测的长时间停顿,不适合需要实时响应的应用场景。
总结
Parallel GC是一种高吞吐量优先的垃圾回收器,适合在多核服务器和大堆内存的环境中使用。它通过并行化垃圾回收任务,提高了系统的吞吐量。然而,由于在垃圾回收时会暂停应用线程,因此不适合对延迟和响应时间要求较高的系统。
三、CMS GC垃圾收集器(JDK1.4)
CMS GC(Concurrent Mark-Sweep Garbage Collector) 是一种低停顿垃圾回收器,专门为减少老年代的垃圾回收停顿时间而设计。CMS是JVM中的一种并发垃圾收集器,从JDK 1.4引入,主要用于对响应时间要求较高的应用,通常适用于有大量老年代对象的大型服务器应用。
CMS GC的工作原理
CMS GC的全称是并发标记-清除垃圾收集器(Concurrent Mark-Sweep),它的垃圾回收过程主要分为以下几个阶段:
-
初始标记(Initial Mark):
- 这一阶段需要暂停所有应用线程(Stop-the-World),从GC Roots出发,标记所有直接可达的对象。由于只标记直接引用的对象,所以这个阶段的停顿时间很短。
-
并发标记(Concurrent Mark):
- 在这一阶段,GC线程和应用线程并发运行,CMS GC在不停止应用线程的情况下标记所有可达对象。这个阶段标记的是从GC Roots到老年代对象的引用链。
-
重新标记(Remark):
- 这一阶段再次暂停所有应用线程,用来标记在并发标记阶段由于应用线程继续运行而导致遗漏的对象。这个阶段也需要Stop-the-World,但是由于只是补充标记,停顿时间相对较短。
-
并发清除(Concurrent Sweep):
- 在这一阶段,CMS GC在应用线程继续运行的情况下,回收那些未被标记为可达的对象,释放内存空间。
CMS GC的特点
-
低停顿:
- CMS的设计目标是减少老年代GC时的停顿时间,它通过并发执行标记和清除操作,尽量减少对应用线程的影响,适合对响应时间敏感的应用。
-
标记-清除算法:
- CMS采用的是标记-清除算法,不像其他垃圾收集器那样进行对象压缩或移动。因此,它不会导致大量对象复制,适合对象数量多的老年代。
-
并发回收:
- CMS回收器的大部分工作都是与应用线程并发进行的,只有在初始标记和重新标记阶段会暂停应用线程(Stop-the-World),因此可以提供相对较短的停顿时间。
-
内存碎片:
- 由于CMS是基于标记-清除的算法,不进行内存压缩,因此可能会产生内存碎片。当内存碎片过多时,会导致堆空间不连续,可能出现无法分配大对象的情况,这时需要执行Full GC(Stop-the-World的全堆压缩和回收)。
-
并发模式失败(Concurrent Mode Failure):
- 如果在并发清除阶段,老年代内存空间不足以容纳新分配的对象,就会触发并发模式失败,导致JVM回退到Serial Old GC(单线程的老年代回收方式),这时会发生长时间的停顿。
CMS GC的优缺点
优点:
- 低停顿时间: 适合对响应时间要求较高的应用,能够显著减少垃圾回收的停顿时间。
- 并发回收: 大部分垃圾回收工作可以与应用线程并发进行,不会阻塞应用程序的正常运行。
缺点:
- 内存碎片: CMS的标记-清除机制不做压缩和整理,容易产生内存碎片,可能会导致内存空间利用率降低,甚至需要进行Full GC。
- CPU资源消耗大: CMS的并发回收阶段会占用额外的CPU资源,尤其是在老年代对象多、回收频繁时,可能影响系统的整体性能。
- Concurrent Mode Failure: 当内存不足时,可能发生并发模式失败,导致系统回退到更慢的Full GC,引发长时间停顿。
CMS GC的配置参数
常用的CMS GC相关的配置参数:
-XX:+UseConcMarkSweepGC
:启用CMS垃圾收集器。-XX:CMSInitiatingOccupancyFraction=<N>
:设置当老年代使用率达到N%时触发CMS垃圾回收,默认值是68%。-XX:+UseCMSInitiatingOccupancyOnly
:强制只在达到设定的使用率时触发CMS,不使用默认的自适应策略。-XX:+CMSClassUnloadingEnabled
:允许CMS回收方法区中的无用类,减少内存泄露风险。-XX:+CMSScavengeBeforeRemark
:在CMS GC的重新标记阶段之前,先触发一次年轻代GC,以减少老年代对年轻代的压力。-XX:+CMSParallelRemarkEnabled
:启用并行重标记阶段,以加快GC速度。
CMS GC的适用场景
CMS GC非常适合那些对响应时间要求高、应用需要较少停顿的场景,例如:
- 高并发的Web应用
- 需要实时响应的金融交易系统
- 用户交互频繁的在线系统
总结
CMS GC通过并发标记和清除的方式减少老年代垃圾回收的停顿时间,使得它在响应时间敏感的应用中非常有用。但它也有内存碎片和CPU占用过高的问题,需要开发者根据具体情况选择适合的垃圾回收器。随着新一代垃圾收集器(如G1 GC和ZGC)的出现,CMS逐渐被淘汰,但在一些旧的高并发应用中,CMS依然被广泛使用。
四、G1 GC垃圾收集器(JDK9)
G1 GC(Garbage-First Garbage Collector) 是一种适用于服务器端应用的低停顿垃圾收集器,旨在替代JDK中的CMS(Concurrent Mark-Sweep)垃圾收集器,从JDK 9开始成为默认的垃圾回收器。G1 GC的设计目标是以可预测的停顿时间控制垃圾回收的影响,同时在大内存环境中有较好的性能表现。
G1 GC的关键特点
-
区域化堆内存管理(Region-based Heap Management):
- G1 GC将整个堆划分为多个大小相同的区域(Region),每个Region既可以存储新生代对象,也可以存储老年代对象。
- 每个Region大小固定,通常是几MB。这种设计使得G1 GC在内存分配和回收时更加灵活。
-
Garbage-First策略:
- G1采用“垃圾优先”(Garbage-First)的策略,优先回收那些包含大量垃圾的Region。这使得它能够根据垃圾的密度选择最优的回收对象,最大化回收效率。
-
混合回收(Mixed GC):
- G1在新生代内存不足时触发常规的年轻代GC,同时也可能会对部分老年代区域进行并行收集,称为混合GC。这种机制可以逐步清理老年代对象,避免Full GC。
-
暂停时间可预测:
- G1允许开发者设定最大GC暂停时间目标(
-XX:MaxGCPauseMillis
),然后根据这个目标动态调整GC行为。它尝试在满足该目标的同时进行尽可能多的内存回收。
- G1允许开发者设定最大GC暂停时间目标(
-
并发与并行回收:
- G1支持并发执行标记、复制等GC工作,这意味着在进行垃圾回收时应用线程可以继续执行,减少了传统GC带来的长时间停顿。
-
压缩与内存碎片管理:
- G1通过并行回收和压缩Region来减少内存碎片,提升内存利用效率,避免CMS中的内存碎片问题。
G1 GC的工作流程
-
年轻代GC(Young GC):
- 仅对新生代Region进行垃圾回收,回收速度快,停顿时间较短。新生代区域被复制到新的Region中(通常是Survivor区或老年代区)。
-
初始标记(Initial Marking):
- 这是G1的并发标记阶段之一,标记从根对象可达的老年代对象。此过程会发生短暂的暂停。
-
并发标记(Concurrent Marking):
- 在这一步中,G1对堆中的对象进行并发标记,找出哪些对象是垃圾,哪些对象仍然活跃。
-
最终标记(Final Remarking):
- 完成标记阶段,对那些在并发标记期间发生变化的对象进行最终标记。此阶段也会发生短暂停顿。
-
清除(Cleanup):
- G1会根据之前的标记结果清理那些包含大量垃圾的Region,并在回收的同时对部分老年代Region进行整理(压缩)。
G1 GC的配置
常用的G1 GC配置参数:
-XX:+UseG1GC
:启用G1垃圾收集器。-XX:MaxGCPauseMillis=<N>
:设置目标最大GC暂停时间(毫秒)。-XX:InitiatingHeapOccupancyPercent=<N>
:设置堆使用率达到多少时触发并发标记周期,默认45%。
G1 GC适用场景
G1 GC适用于以下场景:
- 大内存应用: 特别是在数十GB甚至更大堆内存的情况下,G1表现更好。
- 延迟敏感的应用: 适合对暂停时间要求较高的应用,可以通过设定暂停时间目标来控制GC的影响。
- 内存碎片较严重的系统: 通过区域化内存管理和压缩减少内存碎片问题。
总结
G1 GC通过分区回收、并行与并发的垃圾回收机制、灵活的暂停时间控制,为需要低停顿且大内存应用的Java程序提供了一个高效的垃圾收集解决方案。它在减少GC停顿时间和提高回收效率方面表现出色,同时克服了CMS在老年代GC和内存碎片管理上的不足。
五、ZGC垃圾收集器(JDK11)
ZGC(Z Garbage Collector) 是JDK中的一种低延迟垃圾收集器,旨在处理大内存堆的应用程序,同时保持极低的停顿时间。ZGC 是从 JDK 11 引入的,它主要针对以下需求设计:
- 超大内存堆管理: ZGC 能够处理 TB 级别的堆内存,支持数百GB到TB级的堆大小。
- 极低的停顿时间: ZGC 的停顿时间通常小于 10 毫秒,且与堆大小无关,因此非常适合对响应时间要求极高的应用程序。
- 并行与并发收集: ZGC 主要通过并发执行垃圾回收任务,最大限度减少应用线程的中断。大多数的垃圾回收工作是与应用线程同时进行的。
ZGC的关键特点
-
低延迟: 主要目标是将GC的停顿时间控制在10毫秒以下,几乎不受堆大小的影响。因此,对于那些运行在大内存堆上的应用来说,它能提供非常低的暂停时间。
-
并发处理: 大多数的GC操作(标记、转移等)都是并发进行的,ZGC尽量避免长时间暂停应用线程。
-
区域化内存管理: ZGC将堆划分为不同的区域(regions),并在这些区域中进行垃圾回收和对象分配。与G1类似,但更加灵活。
-
指针压缩: ZGC使用染色指针(colored pointers)来减少内存碎片,并减少对象的移动成本。它不需要复杂的指针转换,使用64位虚拟地址空间来实现更高效的对象管理。
使用ZGC
从JDK 11开始,ZGC可以通过以下命令行参数启用:
java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -Xms<size> -Xmx<size> YourApp
其中,-Xms
和 -Xmx
用于设置堆大小。
ZGC适用场景
ZGC 适合那些需要在低延迟下处理大内存堆的应用,例如:
- 大规模金融交易系统
- 大数据处理
- 需要低延迟的在线游戏
- 需要及时响应的Web应用程序
总结
ZGC是为了应对大规模内存分配和垃圾回收问题而设计的,特别适合对延迟有严格要求的大内存应用。其最大的优势在于无论堆的大小如何,都能保持极低的停顿时间。
六、JDK的5种垃圾收集器配置方法
在JDK中,垃圾收集器的选择和配置对于应用性能的优化至关重要。JVM提供了多种垃圾收集器以适应不同的应用场景。以下是5种常见的垃圾收集器及其配置方法:
1. Serial GC(串行垃圾收集器)
配置方法:
-
启用Serial GC:
-XX:+UseSerialGC
适用场景:
-
适合小型应用、单核CPU或嵌入式设备中,堆内存较小且对响应时间要求不高的场景。
2. Parallel GC(并行垃圾收集器)
配置方法:
-
启用Parallel GC:
-XX:+UseParallelGC
-
启用老年代并行回收(Parallel Old GC):
-XX:+UseParallelOldGC
-
设置并行GC线程数:
-XX:ParallelGCThreads=<N>
适用场景:
-
高吞吐量应用程序,适合在多核系统中使用,适合需要最大化CPU利用率的场景。
3. CMS GC(Concurrent Mark-Sweep Garbage Collector,CMS垃圾收集器)
配置方法:
-
启用CMS GC:
-XX:+UseConcMarkSweepGC
-
设置CMS触发的堆内存占用比例(默认68%):
-XX:CMSInitiatingOccupancyFraction=<N>
-
只在占用率达到设定值时触发CMS:
-XX:+UseCMSInitiatingOccupancyOnly
适用场景:
-
适合需要低停顿时间的场景,如响应时间敏感的Web应用、在线系统。
4. G1 GC(Garbage-First Garbage Collector)
配置方法:
-
启用G1 GC:
-XX:+UseG1GC
-
设置最大GC停顿时间目标(默认200ms):
-XX:MaxGCPauseMillis=<N>
-
设置并行GC线程数:
-XX:ParallelGCThreads=<N>
-
设置老年代并发回收线程数:
-XX:ConcGCThreads=<N>
适用场景:
-
适用于大内存应用,且需要低延迟和高吞吐量的场景。G1 GC将内存划分为多个独立的区域,提供灵活的垃圾回收策略。
5. ZGC(Z Garbage Collector)
配置方法:
-
启用ZGC:
-XX:+UseZGC
-
设置最大堆大小:
-Xmx<size>
-
设置并发GC线程数:
-XX:ConcGCThreads=<N>
适用场景:
-
超低停顿的场景,适用于超大堆内存的系统,尤其是对延迟极其敏感的大型应用。
小结:
- Serial GC:适用于小型应用和单核环境。
- Parallel GC:注重高吞吐量,适用于多核和大内存的应用。
- CMS GC:减少停顿时间,适用于对响应时间要求较高的应用。
- G1 GC:提供良好的停顿时间和吞吐量平衡,适合大内存应用。
- ZGC:专为超大堆内存和低停顿时间设计,适用于超大规模、高并发的应用。
这些垃圾收集器的选择应根据具体的应用需求和硬件资源进行合理配置。