![图片[1]-16 JVM即时编译(上):JIT编译原理与分层编译-速优课](http://www.suyouke.com/wp-content/uploads/2026/07/doubao_img_2304x1728_20260722_174942-1-1024x768.png)
即时编译(JIT)是 JVM 提升性能的核心技术。它将频繁执行的字节码编译为本地机器码,显著提高程序的运行速度。本文将深入探讨 JIT 编译的原理、分层编译策略以及编译器的选择。
一、JIT 编译的基本概念
什么是 JIT 编译
JIT(Just-In-Time)编译是指在程序运行时,将字节码动态编译为本地机器码的过程。与静态编译(如 C++)不同,JIT 编译发生在运行时,可以利用运行时信息进行优化。
JIT 编译的优势
- 运行时优化:可以根据实际执行情况进行优化
- 动态调整:可以根据程序行为调整优化策略
- 跨平台性:保持 Java 的跨平台特性
JIT 编译与解释执行的对比
| 特性 | 解释执行 | JIT 编译 |
|---|---|---|
| 启动速度 | 快 | 慢(需要编译时间) |
| 运行速度 | 慢 | 快(本地机器码) |
| 内存占用 | 低 | 高(需要存储编译后的代码) |
| 优化能力 | 有限 | 强大(可以进行深度优化) |
二、热点代码的识别
什么是热点代码
热点代码是指在程序执行过程中被频繁调用的代码,通常包括:
- 热点方法:被多次调用的方法
- 热点循环:循环体被多次执行
热点代码的判定
JVM 使用方法调用计数器和循环回边计数器来识别热点代码:
- 方法调用计数器:记录方法被调用的次数
- 循环回边计数器:记录循环体被执行的次数
触发 JIT 编译的条件
当方法调用计数器或循环回边计数器超过阈值时,触发 JIT 编译:
- 默认阈值:方法调用 10000 次,循环回边 100000 次
- 可以通过
-XX:CompileThreshold参数调整
三、分层编译策略
分层编译的目的
分层编译是 Java 7 引入的默认编译策略,旨在平衡启动速度和运行性能:
- 快速启动:初始阶段使用解释执行或简单编译
- 逐步优化:随着程序运行,逐步进行深度优化
分层编译的层次
| 层次 | 编译类型 | 优化程度 | 编译速度 |
|---|---|---|---|
| Level 0 | 解释执行 | 无 | 最快 |
| Level 1 | C1 编译(简单优化) | 低 | 快 |
| Level 2 | C1 编译(完全优化) | 中 | 中等 |
| Level 3 | C1 编译(带分析信息) | 中 | 中等 |
| Level 4 | C2 编译 | 高 | 慢 |
分层编译的流程
解释执行(Level 0)
↓
C1 简单编译(Level 1)
↓
C1 完全编译(Level 2)
↓
C1 带分析信息编译(Level 3)
↓
C2 深度编译(Level 4)
四、即时编译器的选择
C1 编译器(Client Compiler)
设计目标:快速编译,适用于客户端应用
优化策略:
- 简单的优化(内联、常量传播等)
- 快速生成代码
- 启动速度快
适用场景:
- GUI 应用
- 桌面应用
- 小程序
C2 编译器(Server Compiler)
设计目标:深度优化,适用于服务器端应用
优化策略:
- 复杂的优化(循环展开、向量化等)
- 生成高度优化的代码
- 运行速度快
适用场景:
- Web 服务器
- 数据库服务器
- 大型企业应用
Graal 编译器
设计目标:通用性和可扩展性
特点:
- 用 Java 实现的编译器
- 支持多种语言
- 实验性编译器(Java 10+)
五、JIT 编译的过程
编译触发
- 方法调用触发:方法调用次数超过阈值
- 循环触发:循环体执行次数超过阈值
- 手动触发:使用
JVMCIAPI 手动触发
编译线程
JVM 使用专门的编译线程进行 JIT 编译:
- 编译线程与应用线程并行执行
- 根据 CPU 核数自动调整编译线程数
- C1 和 C2 编译线程比例为 1:2
编译结果
编译后的机器码存储在代码缓存中:
- 下次调用同一方法时直接使用编译后的代码
- 代码缓存大小可以通过参数调整
- 编译后的代码可以被垃圾回收
六、JIT 编译的监控与调优
JIT 编译的监控
// 使用 jstat 监控 JIT 编译
jstat -compiler <pid>
jstat -printcompilation <pid>
// 使用 JVM 参数输出编译日志
java -XX:+PrintCompilation MyApp
java -XX:+PrintCompilation -XX:+PrintInlining MyApp
JIT 编译的调优参数
| 参数 | 说明 | 默认值 |
|---|---|---|
-XX:CompileThreshold | 触发 JIT 编译的阈值 | 10000 |
-XX:TieredCompilation | 是否启用分层编译 | true |
-XX:CICompilerCount | 编译线程数 | CPU 核心数 |
-XX:+UseC1Compiler | 使用 C1 编译器 | false |
-XX:+UseC2Compiler | 使用 C2 编译器 | true |
-XX:+UseGraalCompiler | 使用 Graal 编译器 | false |
编译日志分析
// 编译日志示例
1 java.lang.String::hashCode (60 bytes)
2 java.lang.String::indexOf (156 bytes)
3 java.util.ArrayList::size (5 bytes) made not entrant
4 java.util.ArrayList::size (5 bytes) made zombie
关键信息:
- 方法名和字节码大小
made not entrant:方法被替换made zombie:方法被废弃
七、总结
JIT 编译是 JVM 提升性能的核心技术:
- 基本概念:运行时将字节码编译为本地机器码
- 热点识别:通过方法调用计数器和循环回边计数器
- 分层编译:平衡启动速度和运行性能
- 编译器选择:C1(客户端)、C2(服务器)、Graal(实验性)
- 监控调优:使用 jstat 和 JVM 参数
理解 JIT 编译的原理,有助于我们写出更高效的代码,并进行有效的 JVM 调优。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END








请登录后查看评论内容