Skip to content

编译执行与解释执行的区别是什么?JVM 使用哪种方式? ​

  • 标签:JVM、Java

🏛 业务架构师 ​

两种执行方式:解释执行(逐条字节码翻译执行,启动快、峰值慢)与编译执行(编译成机器码,首次编译慢、执行快)。

JVM 用混合模式(HotSpot JIT):解释器先跑起来,运行中 profiling 找出"热点代码"(频繁执行的方法与循环),交给 JIT 编译成本地机器码并激进优化(内联、逃逸分析、锁消除),之后全速执行。用启动速度换运行峰值,按二八定律把编译预算花在真正热的 20% 代码上。

业务视角的两个直接影响:

  • 预热问题:应用刚启动时走解释执行,性能差一截,压测和大促都会撞上。落地动作:
    • 生产发布后渐进放量(先 5% 再 50% 再全量),给 JIT 预热时间——这是发布策略的一部分;
    • 大促前用真实流量回放预热,把热点代码提前编译;
    • 对外提供 SLA 的服务,把"预热完成"纳入 readiness 判定;
  • 启动速度的新战场:Serverless/弹性伸缩把"扩容响应时间"变成业务指标,JVM 预热慢的短板被放大。业界的应对(CDS class 数据共享、AOT 静态编译如 GraalVM Native Image、CRaC 检查点恢复)都在削减"解释-编译"过程的成本。代价是 AOT 失去运行时优化的峰值性能与动态能力——"弹性速度 vs 运行峰值"成了新的定价题,Serverless 场景选前者、长稳服务选后者。

一句话:混合模式是 JVM 对"启动快不快、跑得快不快"两全的工程答案,而业务的发布策略、弹性架构要顺着它的特性设计,别让预热的性能坑变成大促当天的容量坑。

🏗 系统架构师 ​

解释执行:把字节码逐条翻译成机器指令执行,启动快、无需编译,但相同代码每次都重新解释,运行慢;编译执行:先把代码整体编译成本地机器码再执行,启动慢/有编译开销,但运行时无翻译成本,跑得快。

JVM 的答案:两者混合(JIT 分层编译):

  • 字节码先由解释器执行(C1 之前零等待启动);
  • 热点探测:方法调用计数器 + 回边计数器,某方法/循环超过阈值(默认约 1 万次调用)判定为热点;
  • 热点代码由 JIT 编译:C1(客户端编译器,快速出中等优化代码)→ 升温后 C2(服务端编译器,重度优化:逃逸分析、锁消除、内联、循环展开)。分层编译(Tiered Compilation,5 层)是现代 JVM 默认;
  • 编译后走本地代码,运行速度接近 C/C++。

架构启示:这是**"冷启动低延迟"与"峰值高吞吐"的动态权衡**——按运行时画像只优化热点(90% 时间花在 10% 代码),避免全量编译的启动代价。同样思想遍布系统设计:数据库对高频 SQL 缓存执行计划、CPU 分支预测缓存、CDN 热内容预热,全是"按需优化高频路径"的变体。GraalVM 的 AOT 则是另一个极端:牺牲运行时自适应,换极致启动速度,适合 Serverless 场景。

💻 全栈开发专家 ​

区别:

  • 编译执行:先整体编译成机器码再运行,运行快但启动慢、不可移植(C/C++)。
  • 解释执行:逐条翻译执行,跨平台、启动快,但每次都翻译,热点代码慢。

JVM 是混合模式(默认 -Xmixed):"javac 编译为字节码"先跨平台,然后 JVM 内部解释器 + JIT 即时编译结合:

  1. 字节码由解释器逐条执行(启动快,无需等编译);
  2. 热点探测:方法调用计数器 / 回边计数器(循环)超过阈值(-XX:CompileThreshold,C2 默认 10000)判定热点;
  3. 热点代码交给 JIT 编译成本地机器码并缓存,之后直接跑机器码;
  4. 分层编译(Tiered Compilation,默认开):C1(客户端编译器,快速出中等优化)先上,继续热再由 C2(服务端,深度优化:内联、逃逸分析、锁消除)重编。

工程关联(加分):

  • 预热问题:低延迟服务刚启动性能差,压测/上线前 warmup(提前触发 JIT);K8s 滚动发布要配置就绪探针延迟,别把冷实例直接放进流量池;
  • JIT 重排是并发的根源之一,也是 JMM 要管的对象;
  • -XX:+PrintCompilation 能看到编译事件,我排查过"方法跑久了反而变快"的疑问,就是分层编译升级了;
  • GraalVM AOT(提前编译)走另一条路:牺牲一点峰值性能换启动速度和低内存,适合 Serverless 场景。