总流程
源程序.java-->编译器javac(javac 源程序.java)-->字节码文件.class-->JVM使程序运行在不同的操作环境上(java 字节码文件.class)
前端编译
源程序.java-->字节码文件.class
把Java源程序.java编译成字节码文件.class的过程
优点:
1.许多Java语法新特性(泛型、内部类等),是靠前端编译器实现的,而不是依赖虚拟机
2.编译成的.class文件可以直接给JVM解释器解释执行,省去编译时间,加快启动速度
缺点:
1.对代码运行效率几乎没有优化措施
2.解释执行效率较低,所以需要结合下面的JIT编译
前端编译器:Oracle javac、Eclipse JDT中的增量式编译器(ECJ)等
之后会由JVM类加载器加载字节码文件,然后通过解释器逐行解释执行,这种方式的执行速度会相对比较慢
后端编译/即时(JIT)编译
字节码文件.class-->机器码
通过Java虚拟机JVM内置的即时编译器(Just In Time Compiler,JIT编译器),在运行时把字节码文件.class编译成本地机器码的过程
有些方法和代码块是经常需要被调用的(也就是所谓的热点代码,Hot Spot Code),所以后面引进了JIT(just-in-time compilation)编译器,JIT属于运行时编译,当JIT编译器完成第一次编译后,会将字节码对应的机器码保存下来,下次可以直接使用,机器码的运行效率显然高于Java解释器
优点:
1.通过在运行时收集监控信息,把热点代码(Hot Spot Code)编译成与本地平台相关的机器码,并进行各种层次的优化
2.可以大大提高执行效率
缺点:
1.收集监控信息影响程序运行
2.编译过程占用程序运行时间(例如使得启动速度变慢)
3.编译机器码占用内存
JIT编译速度及编译结果的优劣,是衡量一个JVM性能的很重要指标,所以对程序运行性能优化集中到这个阶段,也就是说可以对这个阶段进行JVM调优
JIT编译器:HotSpot虚拟机的C1、C2编译器等
HotSpot是一种Java虚拟机,可以实现即时编译,HotSpot虚拟机采用一个解释器和两个编译器混合的方式,能更有效的提高效率,这也解释了我们为什么经常会说Java是编译与解释共存的语言,HotSpot虚拟机内置的两个即时编译器分别为Client Complier(C1)和Server Complier(C2),java源代码被编译器编译成.class字节码文件,java字节码在运行时可以被动态编译成本地代码(前提是解释与编译混合执行模式且虚拟机不是刚启动时)
Client Complier:启动快,占用内存小,执行效率没有server快,默认情况下不进行动态编译,适用于桌面应用程序
Server Complier:启动慢,占用内存大,执行效率高,适用于服务器端应用
由于JIT编译器编译本地代码需要占用程序运行时间,要编译出优化程度更高的代码所花费的时间更长,且解释器可能还要替编译器收集性能监控信息,对解释器执行速度有影响,因此HotSpot采用分层编译(Tiered Compilation)的策略
分层编译:
level 0:interpreter解释执行
level 1:C1编译,无profiling(性能监控)
level 2:C1编译,仅方法及循环back-edge执行次数的profiling
level 3:C1编译,除level 2中的profiling外还包括branch(针对分支跳转字节码)及receiver type(针对成员方法调用或类检测,如checkcast,instnaceof,aastore字节码)的profiling
level 4:C2编译
HotSpot采用了惰性评估(Lazy Evaluation)的做法,根据二八定律,消耗大部分系统资源的只有一小部分的代码(热点代码),也就是 JIT 所需要编译的部分,JVM会根据代码每次被执行的情况收集信息并相应地做出一些优化,因此执行的次数越多,它的速度就越快
静态提前(AOT)编译
字节码文件.class-->机器码
程序运行前,直接把字节码文件.class编译成本地机器码的过程
JDK9引入了一种新的编译模式AOT(Ahead of Time Compilation),直接将字节码编译成机器码,这样避免了JIT预热等各方面的开销
优点:
1.编译不占用运行时间,可以做一些较耗时的优化,并可加快程序启动
2.把编译的本地机器码保存磁盘,不占用内存,并可多次使用
缺点:
1.由于Java语言的动态性(如反射)带来了额外的复杂性,影响了静态编译代码的质量
2.一般静态编译不如JIT编译的质量,这种方式用得比较少
静态提前(AOT)编译器:JAOTC、GCJ、Excelsior JET、ART(Android Runtime)等
不能全部用AOT:和 Java 语言的动态特性有千丝万缕的联系,举个例子,CGLIB动态代理使用的是ASM技术,而这种技术大致原理是运行时直接在内存中生成并加载修改后的字节码文件.class,如果全部使用AOT提前编译,也就不能使用ASM技术了,为了支持类似的动态特性,所以选择使用JIT即时编译器
JDK支持分层编译和AOT协作使用