笔记-2008/05/21-持续更新

https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html

垃圾收集器及其组合

image.png

+XX:+UseSerialGC

开启串行收集器

  • 启动参数
    • +XX:+UseSerialGC
  • 是否默认
    • Client模式的虚拟机默认开启此选项
  • 启动收集器组合
    • 新生代开启Serial
    • 老年代开启Serial Old

  • 新生代Serial GC

串行收集器使用单个线程来执行所有垃圾收集工作,这使得它相对高效,因为线程之间没有通信开销。它最适合单处理器机器,因为它不能利用多处理器硬件,尽管它对于具有小数据集(最多约 100 MB)的应用程序在多处理器上很有用。在某些硬件和操作系统配置上默认选择串行收集器。
如果应用程序不需要备用收集器的特殊行为,请使用串行收集器。串行收集器不是最佳选择的一种情况是在具有大量内存和两个或更多处理器的机器上运行的大型、多线程应用程序。当应用程序在此类服务器级机器上运行时,默认选择垃圾优先(G1)或CMS收集器。

  • 性质

    • Serial 收集器是一种单线程,独占式收集器
  • 算法

    • 新生代区-拷贝算法
  • 优点

    • Serial 收集器不存在多线程所带来的开销
  • 缺点

    • 独占模式导致用户线程暂停GC线程先执行

老年代Serial Old GC

  • 性质

    • Serial 收集器的Old Generation版本(同样是单线程)
  • 算法

    • 老生代区-标记并压缩算法
  • 缺点

    • 在JVM的Server模式下性能不佳,也不能充分利用多CPU的处理能力

应用场景

  • 应用场景1

    • 在JVM的Client模式下使用Serial收集器
  • 应用场景2

    • 在小内存,单核CPU的云平台实例中使用

注意事项

  • GC搭配1

    • JDK1.5中Server模式下默认与Parallel Scavenge 收集器配合使用
  • GC搭配2

    • 当CMS收集器执行失败时,将会作为后备的Old Generation收集器

-XX:+UseParNewGC

ParNew是串行收集器的多线程版本。除了使用多线程之外,它与串行收集器具有相同的其他特性,例如参数、算法、STW等。ParNew它与Parallel Scavenge的不同之处在于它具有增强功能,使其可与CMS一起使用。例如,ParNew执行所需的同步,以便它可以在CMS的并发阶段运行。

开启并行收集器

  • 启动参数
    • -XX:+UseParNewGC
  • 是否默认
    • 关闭
  • 启动收集器组合
    • 新生代开启ParNew
    • 老生代开启Serial Old

新生代ParNew

  • 性质

    • ParNew 收集器是一种多线程,独占式的收集器
  • 算法

    • 新生代区-使用拷贝算法
  • 优点

    • 在多CUP下比Serial 收集器性能更好
  • 缺点

    • 会导致用户线程暂停而让GC线程先执行
    • 在单CUP环境下不会比Serial 的效果好

应用场景

  • 在JVM的Server模式下新生代首选使用的收集器

注意事项

[

](https://openjdk.java.net/jeps/173)
启动 -XX:+UseParNewGC 会产生警告

  1. Java HotSpot(TM) 64-Bit Server VM warning: Using the ParNew young collector with the Serial old collector is deprecated and will likely be removed in a future release

-XX:+UseParallelGC

并行收集器(这里也称为吞吐量优先收集器)是类似于串行收集器的分代收集器。串行收集器和并行收集器之间的主要区别在于并行收集器具有多个线程,用于加速垃圾收集。Parallel Scavenge框架下,默认是在要触发FullGC收集前先执行一次Minor GC,并且两次GC之间能让应用程序稍微运行一小下,以期降低Full GC(次要收集和主要收集)的暂停时间(因为次要收集会尽量清理了新生代的死对象,从而减少了Full GC的工作量)。控制这个行为的VM参数是 -XX:+ScavengeBeforeFullGC

开启吞吐量优先收集器

  • 启动参数
    • -XX:+UseParallelGC
  • 是否默认
    • Server模式的虚拟机默认开启
  • 启动收集器组合
    • 新生代开启Parallel Scavenge GC
    • 老生代开启Parallel Old GC
  • 支持版本
    • 支持版本JDK1.4+

新生代Parallel Scavenge GC

  • 性质

    • 新生代并行回收(Parallel Scavenge) 收集器是一种多线程,独占式的收集器
  • 算法

    • 新生代区-使用拷贝算法
  • 优点

    • 较低的GC频率,可以提升CPU执行时间的吞吐量
  • 缺点

    • 垃圾搜集持续时间较长会导致用户线程明显的暂停

老生代Parallel Old GC

  • 性质

    • Parallel Old 收集器是一种多线程,独占式的收集器
  • 算法

    • 老生代区-使用标记并压缩
  • 优点

    • 较低的GC频率,可以提升CPU执行时间的吞吐量
  • 缺点

    • 垃圾搜集持续时间较长会导致用户线程明显的暂停

应用场景

  • 适合那些在后台运算无需太多实时响应的软件系统
  • 与新生代并行回收收集器配合使用,适合吞吐量敏感的系统

配置微调

并行GC的线程数

  • 参数
    • -XX:ParallelGCThreads=
  • 作用
    • 设置用于年轻代和年老代并行垃圾回收的线程数。默认值取决于 JVM 可用的 CPU 数量。如果CPU少于或等于8个逻辑处理器,将的值设置为逻辑处理器的数量,最多为 8。
  • 默认

    • 如果有8个以上的逻辑处理器,请将 n 的值设置为逻辑处理器的大约 5/8。这在大多数情况下都有效,但较大的 SPARC 系统除外,其中的值大约为逻辑处理器的 5/16。

      启动自适应模式

  • 参数

    • -XX:+UseAdaptiveSizePolicy
  • 默认
    • 启用自适应生成大小调整。默认情况下启用此选项。要禁用自适应生成大小,请明确指定-XX:-UseAdaptiveSizePolicy和设置内存分配池的大小(请参阅-XX:SurvivorRatio选项)。
  • 作用
    • 傻瓜模式,由JVM来自动根据系统运行情况来调整GC的操作
  • 检测顺序
    • 期望的最大 GC 暂停目标
    • 所需的应用程序吞吐量目标
    • 最小的内存占用
  • 工作机制 - 垃圾收集器-XX:+UseParallelGC 默认使用的 -XX:+UseAdaptiveSizePolicy 实现已更改为考虑三个目标
    • 如果 GC 暂停时间大于目标暂停时间,则减少代大小以更好地实现目标。
    • 如果达到暂停时间目标,则考虑应用程序的目标吞吐量。
    • 如果未满足应用程序的目标吞吐量,则增加代的大小以更好地实现目标。 如果同时满足暂停时间目标和吞吐量目标,则减少代的大小以减少占用空间。
  • 原则

    • 只需指定虚拟机的最大堆、配置GC停顿时间、配置吞吐量大小即可

      配置GC停顿时间

  • 参数

    • -XX:MaxGCPauseMillis=
  • 作用
    • 设置最大垃圾收集的停顿时间。这被解释为暗示需要毫秒或更短的暂停时间;默认情况下,没有最大暂停时间目标。如果指定了暂停时间目标,则会调整与垃圾收集相关的堆大小和其他参数,以尝试使垃圾收集暂停时间短于指定值。这些调整可能会导致垃圾收集器降低应用程序的整体吞吐量,并且无法始终满足所需的暂停时间目标。这是一个软目标,JVM 将尽最大努力实现它。
  • 原则
    • 时间较长会降低GC的频率,提升CPU吞吐量
    • 时间较短会增加GC的频率,降低CPU吞吐量

配置吞吐量大小

  • 参数
    • -XX:GCTimeRatio(默认值为99)
    • 它将垃圾收集时间与应用程序时间的比率设置为1 / (1 + )。
  • 作用
    • 设置吞吐量大小,即应用程序耗时/系统总运行时间
  • 默认
    • 参数在0到100之间,默认为99,即CPU的吞吐量为99%,最大1%的GC时间
  • 原则
    • 设置一个可以接收的CPU吞吐量


代规模动态调整

一般不需要手动修改,在每次GC结束时,GC会记录平均暂停时间等统计信息。然后确定GC是否达到目标吞吐量的测试,并对代的规模进行必要的调整。例外情况是,System.gc()显式垃圾收集的统计信息被忽略。

  • -XX:YoungGenerationSizeIncrement= 新生代增长比例
  • -XX:TenuredGenerationSizeIncrement=年老代增长比例
  • -XX:AdaptiveSizeDecrementScaleFactor= 年老代或年老代的收缩因子


默认堆大小设置

除非在命令行中指定了初始和最大堆大小,否则它们是根据机器上的内存量计算的

客户端 JVM 默认初始和最大堆大小

最大为 192 MB 的物理内存,默认值为最大堆大小是物理内存的一半;最大为 1 GB 的物理内存,默认值为物理内存的四分之一。 例如,如果您的计算机有 128 MB 的物理内存,则最大堆大小为 64 MB,大于或等于 1 GB 的物理内存导致最大堆大小为 256 MB。

JVM 不会立即使用最大堆的大小,除非程序创建了足够多的对象需要占用更多的内存。在 JVM 初始化期间分配的数量要小得多,称为初始堆大小。此数量至少为 8 MB,否则为物理内存的 1/64,最大为 1GB 的物理内存大小。 分配给年轻代的最大空间量是总堆大小的三分之一。

服务器 JVM 默认初始和最大堆大小

默认初始和最大堆大小在服务器 JVM 上的工作方式与在客户端 JVM 上的工作方式类似,只是默认值可以更高。在 32 位 JVM 上,如果有 4 GB 或更多的物理内存,则默认的最大堆大小可以高达 1 GB。在 64 位 JVM 上,如果有 128 GB 或更多的物理内存,则默认的最大堆大小可以高达 32 GB。你总是可以通过直接指定这些值来设置更高或更低的初始和最大堆。

指定初始和最大堆大小

可以使用标志-Xms(初始堆大小)和-Xmx(最大堆大小)指定初始和最大堆大小。如果您知道应用程序需要多少堆才能正常工作,可以将-Xms和设置-Xmx为相同的值。如果不是,JVM 将首先使用初始堆大小,然后增大 Java 堆,直到找到堆使用和性能之间的平衡。
其他参数和选项可能会影响这些默认值。要验证您的默认值,请使用该-XX:+PrintFlagsFinal选项并在输出中查找。

CMS(Concurrent Mark Sweep) 收集器

Concurrent Mark Sweep 是一种旨在缩短垃圾收集暂停时间的收集器(响应优先收集器)。顾名思义,它在幕后使用标记扫描算法。由于CMS的目标是实现垃圾收集的短暂暂停,这使得它非常适合运行与最终用户进行大量交互的程序。

STW仍然发生在初始标记阶段和重新标记阶段。由于CMS使用的是mark-sweep算法,它的缺点是GC后会产生大的内存碎片,这反过来又会导致在年老代为大对象分配内存时即使有大量可用内存,也会出现内存不足的问题,这将触发完整的GC。为了克服这个问题,CMS 提供了一个选项 - XX:+UseCMSCompactAtFullCollection,它告诉 CMS 在Full GC完成后执行内存压缩,但这会增加额外的GC暂停。

CMS 收集器在并发收集周期中存在两次暂停应用程序。第一个暂停是将可从根直接访问的对象(例如,来自应用程序线程堆栈和寄存器的对象引用、静态对象等)和堆中的其他地方(例如,年轻代)标记为活动对象。第一次暂停称为初始标记暂停。第二次暂停出现在并发跟踪阶段结束时,它会在 CMS 收集器完成跟踪该对象后查找由于应用程序线程更新对象中的引用而被并发跟踪遗漏的对象。这第二次暂停被称为重新标记暂停。**

启动CMS垃圾收集器

  • 启动参数
    • -XX:+UseConcMarkSweepGC
  • 启动收集器组合
    • 新生代开启ParNew
    • 老生代开启CMS + Serial Old
  • 支持版本
    • JDK1.5

并发模式失败:简单来说就是垃圾回收的速度赶不上创建对象的速度,就STW,并退回到年老代串行回收Serial Old。CMS 收集器使用一个或多个与应用程序线程同时运行的垃圾收集器线程,目的是在老年代变满之前完成它的收集。如前所述,在正常操作中,CMS 收集器在应用程序线程仍在运行的情况下完成大部分跟踪和清除工作,因此应用程序线程只会看到短暂的暂停。但是,如果 CMS 收集器无法在老年代填满之前完成回收不可达对象,或者如果老年代中可用的空闲空间块无法满足分配,那么应用程序将暂停,收集将在所有应用程序线程停止的情况下完成。无法同时完成收集被称为并发模式失败,表示需要调整 CMS 收集器参数。如果并发收集被显式垃圾收集中断(System.gc()) 或为诊断工具提供信息所需的垃圾收集,则报告并发模式中断。

老生代CMS

  • 性质

    • 响应优先垃圾收集器
  • 算法

    • 新生代区-使用使用复制算法
    • 老生代区-使用标记并清除算法
  • 优点

    • 垃圾回收线程的并发性和低停顿性,采用多个阶段增量完成GC
  • 原理

    • 初始标记暂停阶段(Pause Initial Mark) - 第1个暂停是将可从根直接访问的对象和堆中的其他地方标记为活动对象
    • 并发标记阶段(Concurrent Mark)- 该阶段应用程序线程和GC线程同时运行。可能造成对象的关系发生变化。并包含多个子阶段:并发预清理、可中止的并发预清理。
    • 重新标记暂停阶段(Pause Remark) - 第2个暂停查找由于应用程序线程更新对象中的引用而被并发跟踪遗漏的对象
    • *通常在一个并发标记阶段中会发生许多次要收集,应用程序线程在次要收集期间会停止
    • 并发清理阶段(Concurrent Sweep):并发清理阶段的主要工作是清理所有未被标记的死亡对象,回收被占用的空间
    • 并发重置阶段(Concurrent Reset):并发重置阶段,将清理并恢复在CMS GC过程中的各种状态,重新初始化CMS相关数据结构 ,为下一个垃圾收集周期做好准备

应用场景

  • 适合对实时响应较高,GC停顿较短的软件系统,如Web应用

配置微调

并发CMS线程数配置

  • 参数
    • -XX:ParallelCMSThreads
  • 作用
    • 手工设置CMS并发线程数

老年代空间使用率配置

  • 说明
    • 在并发清除阶段用户线程与GC线程并发执行,因此会有新的对象产生(也可能含有待清除的对象)
    • CMS垃圾收集器在运行时仍旧有用户线程产生对象,因此不能等到对内饱和的时候才去执行垃圾清除
  • 参数
    • -XX:CMSInitiatingOccupancyFraction=percent
  • 默认
    • 默认值为68
  • 作用
    • Old Generation使用到多少比例时,触发Old Generation的收集
  • 原则
    • 负载比率较高,则GC频率较低,但可能导致Old Generation内存不足, 因为此时用户线程与GC线程处于并发执行状态,仍会有新的对象产生。 如果此时Old Generation内存不足,将会产生Concurrent Mode Failure, 此时将切换到备用的Serial Old 收集器,进行垃圾收集,性能反而降低。负载比率较低,则GC频率频繁,适用于内存使用率增长很快的应用
  • 适用
    • 该配置仅适用于CMS收集器

老年代内存压缩整理配置

  • 说明
    • 标记并清除算法容易产生内存碎片

碎片整理配置1:

  • 参数
    • -XX:+UseCMSCompactAtFullCollection
  • 默认
    • 默认开启
  • 作用
    • 在CMS收集器完成垃圾收集后启动内存碎片整理
  • 缺点
    • 内存碎片整理导致停机的时间延长,并且不是并发进行的
  • 适用
    • 仅适用于CMS收集器

碎片整理配置2:

  • 参数
    • -XX:CMSFullGCsBeforeCompaction
  • 默认
    • 无默认值
  • 作用
    • 在CMS收集器进行若干次垃圾收集后启动内存碎片整理
  • 原则
    • 尽量减少内存碎片整理的频率
  • 适用
    • 仅适用于CMS收集器

注意事项

G1(Garbage First) 收集器

G1 垃圾收集器(JDK9):这种服务器式收集器适用于具有大量内存的多处理器机器。它以高概率满足垃圾收集暂停时间目标,同时实现高吞吐量。 在某些硬件和操作系统配置上默认选择 G1,或者可以使用-XX:+UseG1GC.

启动G1垃圾收集器

算法特性

  • 老生代区-使用标记并整理
  • 并行:可以有效地使用多处理器,借助多核,可以大大减少Stop The World的时间
  • 分代:G1还是有分代垃圾回收的概念,不同的是它可以自己管理整个JVM堆GC,不需要和其他收集器结合。G1 可以通过将工作分配到多个较小的集合中来逐步压缩老年代中的空间,而不是整体压缩空间,从而减少暂停时间

技术优点

  • 能够平衡CPU吞吐量和较短的停机时间

配置微调

  • 启动G1回收器
    • -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC
  • G1回收器的目标停顿时间
    • -XX:MaxGCPauseMillis=50
    • -XX:GCPauseIntervalMillis=200

注意事项

将被废弃或移除的GC组合

-XX:+UseParallelGC -XX:-UseParallelOldGC

组合方式

Parallel Scavenge + Serial Old

注意事项

    这种组合是不寻常的,因为它将并行年轻代和串行年老代 GC 算法配对。这种组合仅适用于具有非常大的年轻代和非常小的老年代的部署。在这种情况下,由于老年代的规模较小,完整的收集暂停时间可能是可以忍受的。
在实践中,这是一种非常罕见且有风险的部署,因为年轻代中对象的活跃度略有变化将导致OutOfMemoryException,因为老年代明显小于年轻代。与对年轻代和年老代使用并行 GC 算法相比,这种组合的唯一优势是总内存使用量略低。我们认为,这种小内存占用优势(最多约为 Java 堆大小的 3%)不足以超过维护这种 GC 组合的成本。

-XX:-UseParNewGC -XX:+UseConcMarkSweepGC

组合方式

DefNew + CMS

注意事项

Java HotSpot(TM) 64-Bit Server VM warning: Using the DefNew young collector with the CMS collector is deprecated and will likely be removed in a future release

-XX:+UseParallelOldGC

注意事项

当-XX:+UseParallelOldGC单独使用(没有-XX:+UseParallelGC)将选择并行的年轻代和年老代GC算法时将显示警告。

参考资料

  • Java Platform, Standard Edition HotSpot Virtual Machine Garbage Collection Tuning Guide

https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/parallel.html

  • Tuning JVM Garbage Collection for Production Deployments

https://docs.oracle.com/cd/E40972_01/doc.70/e40973/cnf_jvmgc.htm#autoId0

  • HotSpot Virtual Machine Garbage Collection Tuning Guide

https://docs.oracle.com/en/java/javase/11/gctuning/parallel-collector1.html#GUID-DCDD6E46-0406-41D1-AB49-FB96A50EB9CE

  • Garbage First Garbage Collector Tuning

https://www.oracle.com/technical-resources/articles/java/g1gc.html

  • Eclipse OpenJ9

https://www.eclipse.org/openj9/docs/