在 G1 GC 中,一些 JVM 参数对性能至关重要。本文将这些参数分为三类,并提供了生产环境下的常用配置建议。


一、必须配置的参数

这些参数在生产环境中几乎总是需要显式设置,否则 JVM 的默认值可能会不合适。

堆大小设置

我们通常使用 -Xms-Xmx 参数来设定 Java 堆的初始大小和最大大小。一个常见的优化建议是将这两个值设为相同,例如 -Xms4g -Xmx4g。这样做可以避免 JVM 在运行时动态调整堆大小,从而减少可能触发的 Full GC。

在容器化部署环境中(如 Docker 和 Kubernetes),JVM 的默认行为可能会自动根据物理内存的百分比来设置堆大小,这有时会带来问题。

  • -XX:InitialRAMPercentage: 当没有显式设置 -Xms 时,JVM 会根据此参数的值来计算初始堆大小。例如,-XX:InitialRAMPercentage=50.0 会将初始堆大小设为容器可用物理内存的 50%。

  • -XX:MaxRAMPercentage: 类似地,当没有设置 -Xmx 时,此参数决定了最大堆大小。默认值通常为 80%,这意味着 JVM 可能占用容器 80% 的内存,这在资源紧张的环境中可能会导致问题。

GC 日志

GC 日志是排查 JVM 问题的关键。建议在生产环境中总是开启,并指定日志文件路径。

  • JDK 8

    -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/path/gc.log
    
  • JDK 9 及更高版本

    -Xlog:gc*,gc+heap=debug:file=/path/gc.log:time,uptime,level,tags
    

元空间(Metaspace)大小

元空间用于存储类的元数据。如果应用加载的类非常多,默认的元空间大小可能会导致 java.lang.OutOfMemoryError: Metaspace 错误。你可以通过设置 -XX:MaxMetaspaceSize=<size> 来限制元空间的最大大小,防止它无限制地占用系统内存。例如,-XX:MaxMetaspaceSize=512m 是一个常见的设置。

二、强烈推荐配置的参数

这些参数可以让你更好地控制应用的延迟和吞吐量。

  • 延迟目标: 使用 -XX:MaxGCPauseMillis=<milliseconds> 来设置 G1 GC 的最大停顿时间目标。G1 会努力在运行时调整其策略以满足这个目标。默认值为 200 毫秒,但在对延迟要求高的系统中,你可以将其调小,例如设置为 100 毫秒。

  • GC 线程数: G1 使用并行 GC 线程(ParallelGCThreads)和并发 GC 线程(ConcGCThreads)来执行垃圾回收。通常情况下,JVM 会根据 CPU 核心数自动计算最佳线程数。但在具有大量核心(比如 64 核以上)和巨大内存的机器上,过多的 GC 线程可能会占用过多的 CPU 资源,影响应用程序的正常运行。这时,你可能需要手动调整这些参数,减少线程数量。


三、仅在特殊场景下调整的参数

这类参数通常无需配置,除非你遇到特定的性能问题。

  • 晋升阈值: 参数 -XX:InitiatingHeapOccupancyPercent=<percent> 决定了老年代占用堆空间的百分比。当老年代达到这个阈值时,G1 会触发并发标记周期。默认值为 45%。如果你的应用经常发生 Full GC,你可能需要将这个值调低到 30% 到 40% 之间,以提前触发并发回收,避免 Full GC。

  • 大对象(Humongous Object)优化: G1 将超过 Region 大小一半的对象视为 Humongous 对象。这些对象会直接分配到老年代,可能导致内存碎片。如果你知道应用会产生大量此类大对象,可以尝试调整 -XX:G1HeapRegionSize=<size> 来增加 Region 的大小,以减少碎片并提高效率。

  • 防止 Full GC: 在大内存应用中,如果因为老年代空间不足导致晋升失败,可能会触发 Full GC。G1ReservePercent 参数用于设置 G1 预留的老年代空间百分比,以应对这种突发情况。默认值为 10%。在某些极端场景下,你可以适当调高这个值,比如到 15%。


四、总结

对于大多数生产应用,可以重点关注以下参数:

-Xms4g -Xmx4g 
-XX:+UseG1GC 
-XX:MaxGCPauseMillis=200 
-Xlog:gc*:file=/path/gc.log:time,uptime,level,tags