CMS,G1,ZGC三种垃圾回收器的简单记录

309 阅读12分钟
  1. CMS(Concurrent Mark - Sweep)垃圾回收器

    • 基本原理
      • 初始标记(Initial Mark):这是垃圾回收过程的第一步,需要暂停所有的应用线程(Stop - The - World,STW),标记出根对象直接引用的对象。这个阶段速度相对较快,因为只是标记从根节点直接可达的对象,例如,在Java程序中,类的静态变量、栈中的局部变量等作为根对象所直接引用的对象会在这个阶段被标记。

      • 并发标记(Concurrent Mark):在这个阶段,垃圾回收线程与应用线程同时运行。它会基于初始标记阶段标记的对象,通过可达性分析算法,标记出所有从这些对象开始能够到达的对象。这个过程可能会比较长,因为它要遍历整个对象图,但由于是并发进行的,不会让应用线程长时间停顿。例如,在一个复杂的Web应用中,对象之间的引用关系错综复杂,这个阶段就会持续对这些对象进行标记。

      • 重新标记(Remark):这一阶段会再次暂停所有应用线程,主要是为了修正并发标记阶段因为应用线程的运行而可能导致的标记变动情况。由于在并发标记阶段,应用线程可能会更新对象的引用关系,所以需要重新检查这些变化的引用,确保标记的准确性。不过这个阶段的停顿时间通常比初始标记阶段长,但比串行标记整个堆要短得多。

      • 并发清除(Concurrent Sweep):在这个阶段,垃圾回收线程和应用线程也是并发执行的。它会清除那些在标记阶段未被标记的对象,释放内存空间。例如,在内存中有许多已经不再被引用的对象(垃圾对象),这个阶段就会将它们占用的内存回收,以便后续的对象分配使用。

      • 示意图

        image.png

    • 优点
      • 低延迟:由于在并发标记和并发清除阶段,应用线程可以和垃圾回收线程同时运行,所以在这两个阶段不会造成长时间的停顿,对于那些对响应时间要求较高的应用(如Web应用)非常友好,能够有效减少用户的等待时间。
    • 缺点
      • 资源消耗:在并发阶段,虽然应用线程可以继续运行,但垃圾回收线程也在占用CPU资源,这可能会导致应用程序的整体性能下降。而且,在重新标记阶段,由于需要再次暂停应用线程,并且这个阶段的工作量可能比较大,所以可能会出现比较明显的停顿。
      • 内存碎片:CMS采用的是标记 - 清扫算法,在清除阶段只是简单地回收未被标记的对象占用的空间,这可能会导致内存中产生大量的碎片。随着时间的推移,这些碎片可能会使内存分配效率降低,因为当需要分配一个较大的连续内存空间时,可能无法找到合适的空间,即使总的空闲内存空间足够。
    • 适用场景
      • 适用于对响应时间敏感的应用,如Web服务器、B/S架构的应用系统等,这些应用通常不能忍受长时间的垃圾回收停顿。
    • 常用配置
      1. -XX:+UseConcMarkSweepGC

        • 启用 CMS 垃圾回收器。这是使用 CMS 的第一步,告诉 JVM 使用并发标记清除算法进行垃圾回收。
      2. -XX:CMSInitiatingOccupancyFraction

        • 用于设置老年代使用空间达到多少百分比的时候触发 CMS 垃圾回收。例如,设置为 70 表示当老年代使用空间达到 70%时开始进行 CMS 回收。
        • 调优方向:这个值设置得过高可能导致老年代空间不足,引发频繁的 Full GC;设置得过低会导致 CMS 回收过早启动,浪费系统资源。需要根据应用的实际内存使用情况和垃圾产生速度进行调整。
      3. -XX:+UseCMSInitiatingOccupancyOnly

        • 只在达到-XX:CMSInitiatingOccupancyFraction设定的百分比时才触发 CMS 回收,避免 JVM 自动调整触发比例。
      4. -XX:+CMSScavengeBeforeRemark

        • 在 CMS 的重新标记阶段(Remark)之前进行一次 Minor GC,减少重新标记阶段的工作量,从而缩短停顿时间。
      5. -XX:CMSFullGCsBeforeCompaction

        • 设置在进行多少次 Full GC 后对老年代进行碎片整理。CMS 在回收过程中不进行压缩整理,可能会导致内存碎片过多,影响分配效率。通过这个参数可以定期进行碎片整理。
  2. G1(Garbage - First)垃圾回收器

    • 基本原理
      • 分区(Region)概念:G1将堆内存划分为多个大小相等的区域(Region),这些区域可以是Eden区、Survivor区或者Old区。例如,在一个Java堆内存中,可能会划分出几百个这样的区域。这种分区方式使得G1可以对不同的区域采用不同的回收策略,并且能够更加灵活地控制停顿时间。
      • 垃圾回收阶段
        • 年轻代回收(Young GC):主要针对Eden区和Survivor区进行回收。当Eden区满了之后,会触发年轻代回收。在这个过程中,存活的对象会被复制到Survivor区或者直接晋升到Old区。G1的年轻代回收和其他垃圾回收器(如Parallel Scavenge)类似,但由于采用了分区的方式,在内存分配和回收的效率上有所提高。
        • 混合回收(Mixed GC):这是G1特有的回收方式。它会同时回收年轻代和部分老年代的区域。G1会根据各个区域的垃圾堆积程度(通过Remembered Set记录对象的引用信息来判断),优先回收垃圾最多的区域。在混合回收过程中,同样会采用复制算法来移动存活的对象,将它们集中到其他区域,从而腾出空间。
        • 全堆回收(Full GC):一般情况下,G1会尽量避免全堆回收,因为这会导致较长时间的停顿。但如果在系统运行过程中,内存碎片过多或者其他情况导致无法通过混合回收满足内存需求时,就会触发全堆回收。
        • 示意图 image.png
    • 优点
      • 可预测的停顿时间:G1可以通过设置一个期望的最大停顿时间(-XX:MaxGCPauseMillis)来控制垃圾回收的停顿时间。它会根据这个目标,自动调整每次回收的区域数量和回收的优先级,尽量在不超过这个停顿时间的前提下,高效地回收垃圾。例如,在一个对实时性要求较高的金融交易系统中,通过设置合适的最大停顿时间,能够保证系统在垃圾回收过程中依然有较好的响应性能。
      • 高效的内存利用:由于采用了分区的方式,G1在回收过程中可以更好地整理内存空间,减少内存碎片的产生。而且,它能够根据对象的活跃度和垃圾堆积程度,灵活地选择回收区域,使得内存的利用更加高效。
    • 缺点
      • 额外的内存开销:为了维护每个区域的垃圾收集信息(如Remembered Set),G1需要占用一定的额外内存空间。而且,在回收过程中,由于涉及到多个区域之间的对象复制和移动,也会有一定的内存和CPU资源消耗。
      • 复杂性:G1的实现机制相对复杂,它的参数调优也比一些简单的垃圾回收器(如Serial、Parallel)要困难。例如,需要合理地设置分区大小、期望的最大停顿时间等参数,才能充分发挥G1的优势。
    • 适用场景
      • 适用于对停顿时间有严格要求,并且堆内存较大的应用。例如,大型的企业级应用、云计算环境中的应用等,这些应用需要在高并发的情况下,保证系统的稳定性和响应性能。
    • 常用配置
      1. -XX:+UseG1GC

        • 启用 G1 垃圾回收器。
      2. -XX:MaxGCPauseMillis

        • 设置期望的最大垃圾回收停顿时间。G1 会尽力满足这个时间要求,但不保证一定能达到。例如,设置为 200 毫秒表示希望每次垃圾回收的停顿时间不超过 200 毫秒。
        • 调优方向:这个值设置得越小,G1 会更加积极地进行回收,但可能会导致回收次数增加,影响整体性能。需要根据应用的响应时间要求和系统资源进行平衡调整。
      3. -XX:InitiatingHeapOccupancyPercent

        • 设置触发并发标记周期的堆占用百分比。当堆使用空间达到这个比例时,G1 开始进行并发标记。例如,设置为 45 表示当堆使用空间达到 45%时触发并发标记。
        • 调优方向:这个值设置得过高可能导致回收不及时,引发 Full GC;设置得过低会导致并发标记过早启动,浪费系统资源。
      4. -XX:G1HeapRegionSize

        • 设置 G1 分区(Region)的大小。默认情况下,G1 会根据堆大小自动调整 Region 的大小。可以根据应用的对象大小分布和内存使用模式来手动设置 Region 大小。
        • 调优方向:如果应用中有大量大对象,可以适当增大 Region 大小,减少内存碎片;如果对象大小比较均匀,可以设置较小的 Region 大小,提高内存利用率。
  3. ZGC(Z Garbage Collector)垃圾回收器

    • 基本原理

      • 染色指针(Colored Pointers)技术:ZGC的核心创新之一是染色指针技术。它通过在指针中嵌入一些额外的信息(通过编码的方式)来标记对象的状态,如对象是否被移动、是否是垃圾等。这种方式不需要像传统垃圾回收器那样在对象头或者额外的内存区域中记录这些信息,从而减少了内存开销。例如,在一个64位的操作系统中,通过合理地利用指针的高位部分来存储染色信息,实现对对象的标记。

      • 并发处理机制:ZGC几乎所有的垃圾回收阶段都是并发执行的。它包括并发标记、并发准备(为对象的移动做准备)、并发重定位(移动对象到新的内存位置)等阶段。在这些阶段中,应用线程和垃圾回收线程可以同时运行,大大减少了停顿时间。例如,在一个大数据处理应用中,大量的数据对象在内存中,ZGC能够在不影响数据处理进程的情况下,高效地完成垃圾回收。

      • 示意图

        image.png

    • 优点

      • 超低停顿时间:ZGC的设计目标是实现亚毫秒级的停顿时间,这使得它在对延迟要求极高的应用场景中表现出色。例如,在高频交易系统、实时游戏服务器等应用中,即使在垃圾回收期间,也能保证系统的实时响应性能。
      • 大内存支持:ZGC能够有效地处理大容量的堆内存,例如可以支持数TB的堆内存,并且在处理过程中依然能够保持较低的停顿时间。这使得它在处理大型数据中心应用、内存密集型的大数据应用等场景中有很大的优势。
    • 缺点

      • 对CPU资源的依赖:由于ZGC的并发处理机制,在垃圾回收过程中,垃圾回收线程需要和应用线程同时运行,这会占用一定的CPU资源。如果CPU资源不足,可能会影响系统的整体性能,或者导致垃圾回收无法及时完成。
      • 内存开销(相对):虽然染色指针技术减少了部分内存开销,但ZGC在运行过程中仍然需要一定的内存来支持其复杂的并发处理机制和数据结构。例如,它需要一定的内存来存储标记信息、转储信息等,这在一定程度上会增加内存的消耗。
    • 适用场景

      • 适用于对延迟极度敏感,并且需要处理大量内存的应用,如大型数据中心、云计算中的高性能应用、实时性要求极高的金融交易系统和游戏服务器等。
    • 常用配置

      1. -XX:+UseZGC

        • 启用 ZGC 垃圾回收器。
      2. -XX:ZAllocationSpikeTolerance

        • 分配突发容忍度。这个参数用于控制 ZGC 在分配内存时的触发条件。如果在短时间内分配了大量内存,ZGC 会根据这个参数决定是否进行垃圾回收。例如,设置为 2,表示如果在短时间内分配的内存是平时的 2 倍,就可能触发垃圾回收。
        • 调优方向:如果应用中有突发的内存分配需求,可以适当增大这个值,避免频繁触发垃圾回收;如果希望 ZGC 对内存分配更加敏感,可以减小这个值。
      3. -XX:ZCollectionInterval

        • 设置垃圾回收的最小时间间隔。默认情况下,ZGC 会根据系统负载自动调整回收时间间隔。可以通过这个参数手动设置最小时间间隔,防止垃圾回收过于频繁。例如,设置为 10 秒,表示至少每 10 秒才会进行一次垃圾回收。
        • 调优方向:如果应用对停顿时间非常敏感,可以设置较大的值,减少垃圾回收的频率;如果希望及时回收垃圾,可以设置较小的值。
      4. -XX:ZProactive

        • 开启主动垃圾回收。当启用这个参数时,ZGC 会在内存使用达到一定比例之前主动进行垃圾回收,以避免内存压力过大。
        • 调优方向:如果应用的内存使用模式比较稳定,可以开启这个参数,提前回收垃圾,减少停顿时间;如果应用的内存使用波动较大,可以关闭这个参数,让 ZGC 根据实际情况自动触发回收。