Java虚拟机的分级编译是结合C1客户端编译器和C2服务端编译器优势的编译策略,通过不同编译层级的配合,平衡编译耗时和运行性能,对并发场景下的应用性能提升有明显作用。C1编译器编译速度快但优化程度有限,适合短时间运行或冷启动阶段的代码编译;C2编译器优化程度高但编译耗时更长,适合长期运行的热点代码编译。

分级编译的基本原理
JVM的分级编译将代码编译分为多个层级,从解释执行到C1编译、再到C2编译逐步推进。当方法调用次数或循环回边次数达到一定阈值时,会触发对应层级的编译。默认情况下,服务端模式的JVM会开启分层编译,无需手动配置即可使用基础的分级编译能力,但针对并发场景调整参数能获得更好的效果。
常用C1与C2编译器参数
以下是调整分级编译和C1、C2参数的常用JVM选项:
| 参数名称 | 参数作用 | 默认值 |
|---|---|---|
| -XX:+TieredCompilation | 开启或关闭分级编译,默认在服务端JVM中开启 | 开启 |
| -XX:TieredStopAtLevel | 设置分级编译的最高层级,1为仅C1编译,4为C1和C2都启用 | 4 |
| -XX:CompileThreshold | 方法调用次数达到该值触发C1编译,需配合分级编译使用 | 10000 |
| -XX:OnStackReplacePercentage | 循环回边次数占比阈值,达到后触发栈上替换编译 | 140 |
| -XX:C1CompileThreadCount | C1编译器的编译线程数量,影响冷启动阶段的编译效率 | 根据CPU核心数动态调整 |
| -XX:C2CompileThreadCount | C2编译器的编译线程数量,影响热点代码的编译速度 | 根据CPU核心数动态调整 |
并发场景下的参数配置建议
高并发短任务场景
如果应用是大量短生命周期的并发任务,比如接口请求处理类应用,冷启动阶段需要快速编译热点代码,可以适当增加C1编译线程数量,降低C1编译的触发阈值,让热点代码更快进入C1编译状态,同时可以暂时关闭C2编译减少编译资源占用,配置示例如下:
# 高并发短任务场景JVM配置示例 java -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -XX:C1CompileThreadCount=4 -XX:CompileThreshold=5000 -jar your_app.jar
高并发长运行场景
如果是长时间运行的高并发应用,比如消息队列消费者、定时任务调度服务,需要充分发挥C2编译器的深度优化能力,可以适当增加C2编译线程数量,保持分级编译的最高层级为4,让热点代码逐步升级到C2编译,配置示例如下:
# 高并发长运行场景JVM配置示例 java -XX:+TieredCompilation -XX:TieredStopAtLevel=4 -XX:C2CompileThreadCount=2 -XX:CompileThreshold=10000 -XX:OnStackReplacePercentage=140 -jar your_app.jar
参数验证方法
配置完成后可以通过JVM自带的诊断工具验证编译效果,使用jstat -compiler命令可以查看编译器的编译次数、失败次数等信息,使用jstat -printcompilation可以实时查看方法的编译状态,确认C1和C2编译是否正常触发。如果并发场景下编译线程占用CPU过高,可以适当降低对应编译器的线程数量,平衡编译资源和业务线程的资源占用。
注意:不同JDK版本的参数默认值可能存在差异,配置前建议先通过java -XX:+PrintFlagsFinal命令查看当前JDK的默认参数值,再结合实际业务场景调整。