JIT 和 AOT 编译有什么区别?Java 服务为什么需要预热?
下面是一段教学用的模拟面试。
🧑💻 面试官:JIT 和 AOT 有什么区别?
🙋♂️ 我:JIT 在运行时编译,AOT 在运行前编译。
🧑💻 面试官:Java 编译成 class,不就已经是 AOT 了吗?
🙋♂️ 我:要区分字节码编译和机器码编译。
🧑💻 面试官:为什么 Java 服务启动后要预热?换成 Native Image,吞吐一定更高吗?
先说清「编译成什么」和「什么时候编译」。启动速度、稳定吞吐与动态优化,是不同取舍。
面试速答(60 秒版)
JIT 是运行时编译,典型 JVM 会先执行字节码,再对热点方法进行机器码编译,并结合实际运行信息优化。
AOT 则在运行前完成相应编译。面试讨论 Java 本地程序时,通常指提前生成机器码等产物;javac 生成 class 字节码,不能直接等同于 Native Image。
Java 服务预热,是让类加载、热点编译、缓存和连接等状态逐步准备好。刚启动时测到的延迟,不一定代表稳定运行后的性能。
AOT 可以减少某些启动和运行时编译成本,但有构建时间、动态能力和运行环境等取舍,也不保证稳定吞吐更高。实际选择应分别测试冷启动、内存、长期吞吐与尾延迟,而不是只比较启动数字。

图:编译成什么,什么时候编译?。
知识点详解:编译时机,为什么影响服务表现?
javac 与 JIT,不是在重复做同一件事
假设 Java 源码先由 javac 编译成 class。class 中主要是 JVM 字节码,不是目标 CPU 可以直接执行的完整机器码程序。
典型 JVM 可以解释执行字节码,并收集运行信息。某些方法频繁调用后,JIT 把它们编译成机器码,减少后续执行成本。
“Java 已经编译过,为什么还要 JIT”的疑问,来自没有区分编译产物。
JIT 为什么能够利用真实运行情况?
运行时可以知道某个方法是否热点、某个调用通常出现什么类型,以及哪些分支常见。编译器可以利用这些信息做内联等优化。
如果优化依赖的假设后来不成立,JVM 可能去优化,并重新选择执行或编译方式。不能把 JIT 理解成“一旦编译,机器码永远不再改变”。
分层编译和相关机制可以核对 HotSpot 性能增强说明。
预热不仅仅是让 JIT 编译
服务刚启动,还可能加载类、初始化连接池、构建缓存和建立下游连接。JIT 只是其中一部分。
假设压测直接在启动后开始,前一段请求可能承担这些一次性成本。只报告这一段平均耗时,会把冷启动与稳定性能混在一起。
但也不能无限预热直到数字好看。应该明确测量目标:如果用户关心函数冷启动,就测试冷启动;如果关心长期服务,分别报告预热阶段和稳定阶段。

图:这些工作可以交错发生,不是固定的四步流水线;提前编译也不会替业务完成建连和缓存预热。
AOT 减少启动工作,也增加构建与兼容要求
以 GraalVM Native Image 为例,它在构建阶段分析应用并生成本地可执行文件。需要知道可达代码,动态反射、资源和代理等能力可能需要额外配置。
这不是把任意项目按一个按钮就无损变快。依赖库、构建平台、运行时行为及观测能力都需确认,见 Native Image 概述。
JIT 与 AOT 也不是绝对互斥,具体运行时可能有不同组合。应指定实现,而不是把所有语言与虚拟机放进同一结论。
应该怎样做公平比较?
保持业务数据、请求模型、机器资源和功能一致,分别测冷启动到可用、首次请求、稳定吞吐、尾延迟和内存。
同时记录构建成本和运维限制。快速扩缩场景更关注启动,长期高负载服务更关注稳定效率。没有脱离工作负载的通用赢家。
本题是运行时机制,不提供伪等价的 TypeScript/Python 编译代码。
面试官继续追问
为什么压测要跑一段时间?
为了观察状态是否稳定,而不是自动忽略所有慢请求。记录预热长度和稳定判断,必要时单独测试冷态。
AOT 程序完全不需要预热吗?
不一定。运行时编译减少了,缓存、连接和业务初始化仍存在。
吞吐高,代表用户体验好吗?
不能。还要看尾延迟、资源开销和故障时表现,尤其是启动与扩容期间。
面试速记卡
- 编译产物:字节码与机器码分开。
- JIT:运行时热点编译,可利用实际执行信息。
- AOT:提前编译,评估构建和动态能力限制。
- 预热:编译、类加载、连接和缓存都可能参与。
- 比较:冷启动与稳定性能分别测量。
公司面试真题
真题根据求职者公开面经整理,题意经过概括,非逐字原话或公司官方题库;本文为 Sunday 的独立解析。
阿里巴巴 · Java后端 · 社招
JIT、分层编译与逃逸分析怎样工作?(题意整理)
社招一年半面经分享 · 阿里部分 ↗
历史面经,面试年份未明确;页面编辑于 2024-07-19