性能优化 - 高级进阶:JVM 常见优化参数
文章目录
- Pre
- 1. 引言:Java版本演进与GC演变背景
- 2. 查看JVM参数默认值的方法
- 3. 基于ElasticSearch的JVM参数示例分析
- 3.1 堆空间配置与AlwaysPreTouch
- 3.2 堆外内存细分
- 3.3 GC日志配置(Java 8 vs Java 9+)
- 3.4 OOM时Dump配置
- 3.5 垃圾回收器配置:CMS示例及迁移
- 3.6 其他额外参数
- 4. 常见GC器演进与配置要点
- 4.1 CMS生命周期与参数
- 4.2 G1配置要点
- 4.3 ZGC、Shenandoah简介
- 4.4 其他GC(Serial、Parallel等)
- 5. 深入调优:分代比例、SurvivorRatio、TenuringThreshold等
- 6. 小结与建议
Pre
性能优化 - 理论篇:常见指标及切入点
性能优化 - 理论篇:性能优化的七类技术手段
性能优化 - 理论篇:CPU、内存、I/O诊断手段
性能优化 - 工具篇:常用的性能测试工具
性能优化 - 工具篇:基准测试 JMH
性能优化 - 案例篇:缓冲区
性能优化 - 案例篇:缓存
性能优化 - 案例篇:数据一致性
性能优化 - 案例篇:池化对象_Commons Pool 2.0通用对象池框架
性能优化 - 案例篇:大对象的优化
性能优化 - 案例篇:使用设计模式优化性能
性能优化 - 案例篇:并行计算
性能优化 - 案例篇:多线程锁的优化
性能优化 - 案例篇:CAS、乐观锁、分布式锁和无锁
性能优化 - 案例篇: 详解 BIO NIO AIO
性能优化 - 案例篇: 19 条常见的 Java 代码优化法则
性能优化 - 案例篇:JVM垃圾回收器
性能优化 - 案例篇:JIT
性能优化 - 案例篇:11种优化接口性能的通用方案
1. 引言:Java版本演进与GC演变背景
Java 8长期以来是企业主力版本,很多公司因稳定性和兼容性选择Java 8并启用CMS垃圾回收器。但从Java 9开始进入半年一版的快速发布模式,Java 8与Java 11成为LTS版本,后续LTS如Java 17、Java 21等陆续推出([en.wikipedia.org][1], [preemptive.com][2])。
随着JVM内部不断演进,CMS在Java 9中被标记为Deprecated,并于Java 14正式移除(见JEP 363)([openjdk.org][3], [oracle.com][4])。新GC器如G1、ZGC、Shenandoah成熟,逐渐成为主流选择。
因此,在不同JVM版本上配置GC参数需先查看默认值,不要盲从他人建议。
2. 查看JVM参数默认值的方法
可以使用以下命令查看参数的默认值和当前生效参数:
java -XX:+PrintFlagsFinal -XX:+UseG1GC 2>&1 | grep UseAdaptiveSizePolicy
上述命令在启动时列出所有Flags的最终值,通过grep过滤目标参数。常见用法还有:
java -XX:+PrintCommandLineFlags -version
输出示例(Java 8默认Parallel GC):
-XX:InitialHeapSize=127905216 -XX:MaxHeapSize=2046483456 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC
openjdk version "1.8.0_41"
...
通过这些命令可确认某个参数是否已有默认值,避免重复配置或无效配置。
3. 基于ElasticSearch的JVM参数示例分析
ElasticSearch(ES)作为典型Java应用,其jvm.options提供了常见配置案例,便于学习和参考。
3.1 堆空间配置与AlwaysPreTouch
ES常见配置:
-Xms1g
-Xmx1g
-XX:+AlwaysPreTouch
将-Xms与-Xmx设为相同值可避免动态扩容开销。在堆较大时,-XX:+AlwaysPreTouch
会在JVM启动时触碰(touch)所有堆页面,提前向操作系统申请并锁定内存页,增加启动时间,但减少运行时动态分配时的性能损耗,提高稳定性和吞吐([docs.oracle.com][5])。
对于存储型服务(如ES),通常将堆设置为物理内存一半,以留一半给操作系统文件缓存(PageCache),提高I/O效率;对于计算型Web服务,可将堆设置为物理内存2/3,剩余1/3供直接内存、本地内存等使用。
JVM内存布局:堆与堆外细分、PageCache与JVM堆的关系。 堆外包含元空间、CodeCache、直接内存、本地内存(线程栈、网络缓冲等)及JNI分配等。
3.2 堆外内存细分
- 元空间(Metaspace):通过
-XX:MetaspaceSize
和-XX:MaxMetaspaceSize
可指定初始化与上限;默认无上限可能导致过度使用系统内存,建议根据应用类加载行为适当设置上限。 - JIT编译后代码存放 CodeCache:JIT编译后代码存储区,通过
-XX:ReservedCodeCacheSize
设置上限;若CodeCache满,会触发Full GC或影响性能。 - 直接内存(Direct Memory):ByteBuffer等直接缓冲区申请内存,通过-
XX:MaxDirectMemorySize
限制,防止内存占用过大。 - 本地内存(Native Memory):线程栈、JNI malloc、网络连接缓冲等,无法由JVM直接控制,但需关注高并发场景下的本地内存消耗。
- JNI内存:依赖具体JNI实现,JVM无法控制,但应避免JNI库出现内存泄漏。
3.3 GC日志配置(Java 8 vs Java 9+)
在Java 8中,常用GC日志参数(以ES为例):
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-XX:+PrintTenuringDistribution
-XX:+PrintGCApplicationStoppedTime
-XX:-Xloggc:logs/gc.log
-XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFiles=32
-XX:GCLogFileSize=64m
这些参数可打印GC细节、时间戳(系统时间)、年龄分布、STW停顿时间,并配置日志滚动([docs.oracle.com][5])。
Java 9+移除40多个GC日志参数,采用Unified Logging:
-Xlog:gc*,gc+age=trace,safepoint:file=logs/gc.log:utctime,pid,tags:filecount=32,filesize=64m
此方式更统一、灵活,可通过jcmd动态调整([foojay.io][6])。
3.4 OOM时Dump配置
ES常见:
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=data
-XX:ErrorFile=logs/hs_err_pid%p.log
发生OOM时自动生成堆转储文件,用于后续MAT等工具分析问题根因。正常可使用jmap等命令获取堆快照,但生产环境优先配置自动Dump。
3.5 垃圾回收器配置:CMS示例及迁移
ES默认曾使用CMS:
-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=75
-XX:+UseCMSInitiatingOccupancyOnly
- UseConcMarkSweepGC表示Young代用ParNew,Old代用CMS。
- CMSInitiatingOccupancyFraction设定启动并发标记的老年代占用阈值,避免Concurrent Mode Failure;需配合UseCMSInitiatingOccupancyOnly生效。
另外,对于 CMS 垃圾回收器,常用的还有下面的配置参数:
-XX:+ExplicitGCInvokesConcurrent
-XX:CMSFullGCsBeforeCompaction=0
-XX:+CMSScavengeBeforeRemark
-XX:+ParallelRefProcEnabled
-
-XX:ExplicitGCInvokesConcurrent
当代码里显示的调用了 System.gc(),实际上是想让回收器进行FullGC,如果发生这种情况,则使用这个参数开始并行 FullGC。建议加上。 -
-XX:CMSFullGCsBeforeCompaction
默认为 0,就是每次FullGC都对老年代进行碎片整理压缩,建议保持默认。 -
-XX:CMSScavengeBeforeRemark
开启或关闭在 CMS 重新标记阶段之前的清除(YGC)尝试。可以降低 remark 时间,建议加上。 -
-XX:+ParallelRefProcEnabled
可以用来并行处理 Reference,以加快处理速度,缩短耗时。
但CMS在Java 14已移除,不可再用,应转向G1或更现代GC([blog.gceasy.io][7])。
3.6 其他额外参数
-Xss1m
:设置线程栈大小为1MB,默认即1MB,可根据线程数和调用深度适当调整。-XX:-OmitStackTraceInFastThrow
:ES关闭此项以便调试异常;开启可合并重复异常栈以提高性能,但调试困难。-Djava.awt.headless=true
:服务器无GUI时需设置Headless模式,避免AWT初始化失败。- 其他-D参数如
-Dfile.encoding=UTF-8、-Des.networkaddress.cache.ttl=60
等,根据应用需求配置网络、编码、临时目录、日志框架等。
4. 常见GC器演进与配置要点
4.1 CMS生命周期与参数
CMS自Java 1.4.1起被引入,目标低延迟,但不压缩老年代容易产生碎片、发生Concurrent Mode Failure导致长STW。Java 9开始Deprecated,Java 14移除(JEP 363)([openjdk.org][3], [oracle.com][4])。建议迁移至G1等。
4.2 G1配置要点
G1自Java 7u14实验性引入,Java 9起默认GC。优点:并发标记、分区回收、自动压缩,提供MaxGCPauseMillis目标设置。常见参数:
-XX:+UseG1GC
-XX:MaxGCPauseMillis=<期望毫秒>
-XX:G1HeapRegionSize=<2的幂,默认通常合适,不建议轻易修改>
-XX:InitiatingHeapOccupancyPercent=<启动并发标记阈值,默认45%>
-XX:ConcGCThreads=<默认即可>
G1无需手动设置-Xmn,自动调优分代大小;可通过打印日志分析GC行为,调整Pause目标和Region大小。
4.3 ZGC、Shenandoah简介
ZGC
:Java 11实验性,Java 15生产就绪,面向超大堆和低停顿(通常<10ms)。使用-XX:+UseZGC启用,需JDK对应版本支持。Shenandoah
:类似目标,Java 12实验,Java 15+成熟。
这些GC适用于大内存、低延迟场景,但需确认应用和平台支持。
4.4 其他GC(Serial、Parallel等)
- Serial GC(
-XX:+UseSerialGC
):小堆、单线程场景。 - Parallel GC(
-XX:+UseParallelGC / UseParallelOldGC
):追求吞吐,适合批处理。
选择需根据应用类型(交互型、批处理、超大堆等)决定。
5. 深入调优:分代比例、SurvivorRatio、TenuringThreshold等
-Xmn
:调整年轻代大小,G1一般无需;在Parallel或CMS可根据业务调整,若对象存活率低可加大年轻代。-XX:SurvivorRatio
:Eden和Survivor区比例,默认8,可根据对象晋升行为调整。-XX:MaxTenuringThreshold
:对象晋升阈值,CMS下默认6,G1下默认15;可通过-XX:+PrintTenuringDistribution
观察年龄分布,若早期代晋升过多,适当降低阈值;若大多数对象在Survivor区很快死亡,适当增大可减少晋升压力。PretenureSizeThreshold
:大对象直接分配到老年代,使用少见。
通过GC日志分析或可视化工具(如GCViewer、GCEasy)观察GC停顿、年轻代/老年代使用情况,再针对性调整。
6. 小结与建议
- 在不同JVM版本和GC器上,先查看默认参数,不要盲信旧配置;
- 根据应用类型(存储、计算、低延迟、高吞吐)选择合适堆大小及GC器;
- 通过
-XX:+PrintCommandLineFlags、PrintFlagsFinal、-Xlog
或旧日志参数查看生效配置; - 合理配置元空间、CodeCache、直接内存等,防止本地内存耗尽;
- 配置GC日志和OOM自动Dump,便于问题排查;
- CMS已废弃,迁移至G1或更现代GC;
- 深入调优需结合GC日志分析工具进行,调整分代比例、停顿目标、并发线程数等;
- 在生产环境中小幅、渐进式调整,观察效果。
[1]: https://en.wikipedia.org/wiki/Java_version_history?utm_source=chatgpt.com "Java version history"
[2]: https://www.preemptive.com/blog/the-road-to-java-9/?utm_source=chatgpt.com "The Road to Java 9 - PreEmptive Solutions"
[3]: https://openjdk.org/jeps/363?utm_source=chatgpt.com "JEP 363: Remove the Concurrent Mark Sweep (CMS) Garbage ..."
[4]: https://www.oracle.com/java/technologies/javase/14-relnote-issues.html?utm_source=chatgpt.com "JDK 14 Release Notes - java - Oracle"
[5]: https://docs.oracle.com/en/java/java-components/enterprise-performance-pack/epp-user-guide/printing-jvm-information.html?utm_source=chatgpt.com "3 Printing JVM Information - Java - Oracle Help Center"
[6]: https://foojay.io/today/introduction-to-jvm-unified-logging-jep-158-jep-271/?utm_source=chatgpt.com "Introduction to JVM Unified Logging (JEP-158 / JEP-271) - Foojay.io"
[7]: https://blog.gceasy.io/cms-gc-algorithm-removed-from-java-14/?utm_source=chatgpt.com "CMS GC algorithm removed from Java 14?"