关于JIT编译的思考

303 阅读3分钟

.java通过javac变为.class的字节码文件,JVM加载.class字节码,在经过解释执行的方式运行代码。对于一些热点代码,会直接编译为机器码直接执行,通过编译执行的方式运行代码。从效率上将,我们认为编译执行的速度远大于解释执行

那么问题就来了,为什么不将所有代码都进行编译执行?

等等,都编译?C不就是等所有都编译然后在执行么。所以从语言层面来看,Java如果想要从语言层面速度更快,不得不去对标底层的C语言。

了解到以下几个问题可以让你思考到背后的原因:

在多个操作系统的时代,你在window系统上用C语言开发了一个非常好用的内存清理工具。随着工具火爆,使用Linux系统的人也想尝试这款工具。但是由于Linux与window操作系统指令的差异,你不得不得在Linux中将涉及对应指令的地方重写一遍。从而导致一个软件需要维护两套代码。

所以,Java的出现就是想解决这个问题,不管在任何平台只要用Java语言开发出的软件,能够在任何平台得以运行。那么Java是如何做到的?没有什么事情不能用一个中间层解决,如果不能就用两个。 Java就采用了一个中间层的方式实现该目的。

C语言需要经过编译->执行。先通过平台(如window/Linux)编译成一个平台的可执行文件,然后在平台上运行。Java则是 源代码 -> 编译.class -> 执行,将.java源码编译为中间层.class字节码,然后在每个平台上编写一个执行.class字节码的程序(即JVM)。所以在这个架构中,JVM需要读取.class字节码进行解析,因此将Java设计成解释执行是一个很好的方式。

但JVM能否让.class编译执行呢?当然可以啦:-Xcomp JVM参数。可以让JVM关闭解释执行,先编译为机器码然后执行。

但这种方式会更高效吗?答案是肯定的,执行机器码与执行字节码让CPU来选肯定是选机器码。但是这会带来额外的开销。比如空间分配不会是动态的,会直接分配设置的内存大小。导致JVM启动也会慢很多。如果资源充足,这也是一个优化方向。

在解释编译的选择下,为了增加执行速度,JVM增加了运行时编译(JIT)的特性,对于热点代码将其转为机器码执行。所以为了判定热点代码、编译热点代码,需要有额外的性能消耗。

为了减少判断热点代码、编译热点代码的性能损失,JDK9添加了AOT的特性,如果你已经知道哪些代码是热点代码,或者你要让某段代码以机器码运行,可以提前将其编译为机器码。然后JVM运行时加载机器码而不是加载.class。