Java执行机制

本文介绍了Java的动态编译机制

Java执行机制

首先,Java平台如今的主流版本(JDK8以上,HotSpot VM)中,主要采用混合模式进行代码执行。

Java主要使用JIT动态编译解释器混合执行。

JIT编译器,英文写作Just-In-Time Compiler,中文意思是即时编译器。

JIT 编译过程

当 JIT 编译启用时(默认是启用的),JVM 读入.class 文件解释后,将其发给 JIT 编译器。JIT 编译器将字节码编译成本机机器代码。

具体来说,Java有热点代码编译功能,通过统计热点代码,将热点代码通过JIT编译成机器码交并存入Code Cache,而其余代码则由JVM解释执行。

图片参考《码出高效》一书

Hot Spot 编译

当 JVM 执行代码时,它并不立即开始编译代码。这主要有两个原因:

首先,如果这段代码本身在将来只会被执行一次,那么从本质上看,编译就是在浪费精力。因为将代码翻译成 java 字节码相对于编译这段代码并执行代码来说,要快很多。

当然,如果一段代码频繁的调用方法,或是一个循环,也就是这段代码被多次执行,那么编译就非常值得了。因此,编译器具有的这种权衡能力会首先执行解释后的代码,然后再去分辨哪些方法会被频繁调用来保证其本身的编译。其实说简单点,就是 JIT 在起作用,我们知道,对于 Java 代码,刚开始都是被编译器编译成字节码文件,然后字节码文件会被交由 JVM 解释执行,所以可以说 Java 本身是一种半编译半解释执行的语言。Hot Spot VM 采用了 JIT compile 技术,将运行频率很高的字节码直接编译为机器指令执行以提高性能,所以当字节码被 JIT 编译为机器码的时候,要说它是编译执行的也可以。也就是说,运行时,部分代码可能由 JIT 翻译为目标机器指令(以 method 为翻译单位,还会保存起来,第二次执行就不用翻译了)直接执行。

第二个原因是最优化,当 JVM 执行某一方法或遍历循环的次数越多,就会更加了解代码结构,那么 JVM 在编译代码的时候就做出相应的优化。

举一个简单的例子:我们知道 equals()这个方法存在于每一个 Java Object 中而且经常被覆写。当解释器遇到b = obj1.equals(obj2) 这样一句代码,它则会查询 obj1 的类型从而得知到底运行哪一个 equals() 方法。而这个动态查询的过程从某种程度上说是很耗时的。

具体案例

放到具体生产环境,这种执行方式造成了机器在热机状态可以承受的负载要大于冷机状态。

如果以热机状态时的流量进行切流,可能使处于冷机状态的服务器因无法承载流量而假死。

案例:

某程序员在发布平台进行分批发布,在输入发布总批数时,误填写成两批发布,

如果是热机状态,正常情况下一半的机器可以勉强承载流量,但由于刚启动时的JVM均为解释执行,没有进行热点代码统计和JIT动态编译,导致当前1/2发布成功的服务器马上全部宕机。

参考