主题
面试速答(先看这里)
**一句话结论:**AOT编译 ,翻译一下就是提前编译,它不像JIT一样在运行期才生成机器码,而是在编译期间就将字节码转换为机器码,这就直接省去了运行时对JVM的依赖。
60秒标准回答:
我们都知道,Java中有两种编译方法
1、javac把java代码编译成字节码,然后由Java虚拟机解释执行
2、JIT把java的字节码直接编译成机器码,然后由Java虚拟机直接运行
**答题顺序:**结论 → 原理/机制 → 关键流程 → 场景与取舍 → 易错点
回答主线:
- **要点1:**2、JIT把java的字节码直接编译成机器码,然后由Java虚拟机直接运行。
- **要点2:**但是,JIT编译有一些比较明显的缺点也是不能忽视的:
- **要点3:**而在如今云原生盛行的今天,应用的快速启动以及减少预热时长是非常重要的,特别是Serverless场景中,所以, 一 个新兴的编译器GraalVM就诞生了。
- **要点4:**因为AOT编译是在编译期就生成机器代码了,所以,应用启动时就不需要编译,那么他就可以大大的减少应用的启动时间,提升系统的整体性能。
- **要点5:**1、机器执行的时候执行的是经过编译优化的本地代码。
**记忆锚点:**Java → JIT → java → AOT → javac → native
关键取舍:
- 可能会影响应用性能 :JIT 编译是需要进行热点代码检测、代码编译等动作的,这些都是要占用运行期的资源,所以,JIT编译过程中也可能会影响应用性能。
- 详见: 因为AOT编译是在编译期就生成机器代码了,所以,应用启动时就不需要编译,那么他就可以大大的减少应用的启动时间,提升系统的整体性能。
- 所以他非常适用于对启动时间敏感的场景,例如云原生应用。
易错提醒:
- 但是,JIT编译有一些比较明显的缺点也是不能忽视的: 增加启动时间 :由于JIT编译器在程序运行时编译代码,它可能导致应用程序的启动时间较长。
- 静态编译的局限性 一个Java程序之所以能做静态编译,需要满足一个至关重要的前提—— 封闭性假设 。
加分表达:
- 而在如今云原生盛行的今天,应用的快速启动以及减少预热时长是非常重要的,特别是Serverless场景中,所以, 一 个新兴的编译器GraalVM就诞生了。
- 与传统的Java的运行时编译(动态编译/jit)相比,静态编译主要有几个重要的优势: 1、机器执行的时候执行的是经过编译优化的本地代码。
追问准备:
- 围绕「Java」:底层原理是什么?使用时有哪些边界和常见坑?
- 围绕「JIT」:底层原理是什么?使用时有哪些边界和常见坑?
- 围绕「java」:底层原理是什么?使用时有哪些边界和常见坑?
- 如果线上出现异常,你会如何定位、验证并规避?
典型回答
我们都知道,Java中有两种编译方法:
1、javac把java代码编译成字节码,然后由Java虚拟机解释执行。
2、JIT把java的字节码直接编译成机器码,然后由Java虚拟机直接运行。
但是,JIT编译有一些比较明显的缺点也是不能忽视的:
- 增加启动时间:由于JIT编译器在程序运行时编译代码,它可能导致应用程序的启动时间较长。
- 可能会影响应用性能:JIT编译是需要进行热点代码检测、代码编译等动作的,这些都是要占用运行期的资源,所以,JIT编译过程中也可能会影响应用性能。
而在如今云原生盛行的今天,应用的快速启动以及减少预热时长是非常重要的,特别是Serverless场景中,所以,一个新兴的编译器GraalVM就诞生了。他提出了一种新的编译方式——Ahead of Time,即AOT编译。
AOT编译,翻译一下就是提前编译,它不像JIT一样在运行期才生成机器码,而是在编译期间就将字节码转换为机器码,这就直接省去了运行时对JVM的依赖。这是一种典型的静态编译技术。
> java 静态编译,是指将java程序的字节码在单独的离线阶段编译为汇编代码,它的输入是Java的字节码,输出是native image。
这里多说一句,很多人会误以为编译期的编译不是javac吗?其实javac只是把源代码(.java)编译成中间代码(.class),这种中间代码我们叫做字节码,而字节码并不是机器代码,无法直接执行,需要解释器进行解释执行。详见:
因为AOT编译是在编译期就生成机器代码了,所以,应用启动时就不需要编译,那么他就可以大大的减少应用的启动时间,提升系统的整体性能。所以他非常适用于对启动时间敏感的场景,例如云原生应用。

扩展知识
静态编译的优势
与传统的Java的运行时编译(动态编译/jit)相比,静态编译主要有几个重要的优势:
1、机器执行的时候执行的是经过编译优化的本地代码。执行本地代码可以非常的高效和快速,并不需要进行解释执行和JIT编译,就可以直接执行。
2、静态编译后的可执行程序自包含了轻量级运行时支持,所以他在运行时不再需要依赖额外的JVM。
3、解决应用程序的冷启动问题。有了静态编译后的本地代码,应用程序就可以快速地启动,不再解释执行,也不再需要JIT的预热。
4、打破Java程序与本地代码之间的边界。因为编译后的代码也是本地代码,所以JNI调用的开销更低了。
静态编译的局限性
封闭性假设
一个Java程序之所以能做静态编译,需要满足一个至关重要的前提——封闭性假设。
什么是封闭性假设,也就是说他要求所有运行时的内容必须在编译时可见,并且可以被编译到native image中。但是Java中有很多代码是没有办法在编译期就确定的,比如反射,他就是不满足封闭性假设的。
其他的类似的违反封闭性假设的特性还有动态代理、序列化、JNI、动态类加载等等。所以,遇到这些代码的时候,就需要额外的适配来解决。带来了很多的复杂性,也给静态编译带来了一定的局限性。
平台相关性
Java有一个很重要的特性就是平台无关性,但是随着静态编译的盛行,这个说法已经并不一定就成立了。
静态编译以后的代码程序,其实就是平台相关的了。