java编译器面试:Java虚拟机的即时编译
java编译器面试:Java虚拟机的即时编译HotSpot 虚拟机中使用了基于计数器的热点探测,为了实现热点计数,HotSpot 为每个方法准备了两类计数器:调用计数器(Invocation Counter)和回边计数器(Back Edge Counter),“回边”的意思就是指在循环边界往回跳转。当虚拟机运行参数确定的情况下,这两个计数器都有一个明确的阈值,计数器阈值一旦溢出,就会触发即时编译。要知道某段代码是不是热点代码,是不是需要触发即时编译,这个检测行为就是“热点探测”(Hot Spot Code Detection)。Java代码的编译过程Java 程序最初都是通过解释器(Interpreter)进行解释执行的,当虚拟机发现某个方法或代码块的运行特别频繁,就会把这些代码认定为“ 热点代码 ”(Hot Spot Code),为了提高热点代码的执行效率,在运行时,虚拟机会把这些代码编译成本地机器码,并尽可能地进行代码优化,这个
什么是即时编译从虚拟机视角来看,执行 Java 代码首先需要将它编译而成的 class 文件加载到 Java 虚拟机中。加载后的 Java 类会被存放在方法区(Method Area)中。实际运行时,虚拟机会执行方法区内的代码。
从硬件视角来看,Java 字节码无法直接执行。因此,Java 虚拟机需要将字节码翻译成机器码。
在 HotSpot 里面,上述翻译过程有两种形式:第一种是解释执行,即逐条将字节码翻译成机器码并执行;第二种是即时编译(JIT: Just-In-Time compilation),即将一个方法中包含的所有字节码编译成机器码后再执行。
即时编译后的代码也会被保存到方法区中,当这个方法再次被调用时,就省去了解释的过程,直接执行即时编译缓存的机器码,可以提升程序运行性能。
Java代码的编译过程
Java 程序最初都是通过解释器(Interpreter)进行解释执行的,当虚拟机发现某个方法或代码块的运行特别频繁,就会把这些代码认定为“ 热点代码 ”(Hot Spot Code),为了提高热点代码的执行效率,在运行时,虚拟机会把这些代码编译成本地机器码,并尽可能地进行代码优化,这个过程就叫即时编译,运行时完成这个任务的后端编译器被称为即时编译器。
哪些代码是热点代码?热点代码主要包括两类:
- 被多次调用的方法
- 被多次执行的循环体
要知道某段代码是不是热点代码,是不是需要触发即时编译,这个检测行为就是“热点探测”(Hot Spot Code Detection)。
HotSpot 虚拟机中使用了基于计数器的热点探测,为了实现热点计数,HotSpot 为每个方法准备了两类计数器:调用计数器(Invocation Counter)和回边计数器(Back Edge Counter),“回边”的意思就是指在循环边界往回跳转。当虚拟机运行参数确定的情况下,这两个计数器都有一个明确的阈值,计数器阈值一旦溢出,就会触发即时编译。
为什么不把字节码全部提前编译?提前编译(Ahead Of Time,AOT)和即时编译这两种方法相比,提前编译的优点是启动更快,即时编译因为字节码第一次运行还需要编译,启动性能会慢一些,不过峰值运行性能是差不多的。
至于JVM为什么不采用AOT的方法,主要是考虑字节码的可移植性,而牺牲了启动性能。在不提前编译的情况下,程序需要 JIT 预热后,才能达到最高性能。
JIT编译器与解释器如何协作目前主流的 Java 虚拟机,比如 HotSpot、OpenJ9 等,内部都同时包含解释器与编译器,解释器与编译器两者各有优势:
- 当程序需要迅速启动和执行的时候,解释器可以首先发挥作用,省去编译的时间,立即运行。
- 当程序启动后,随着时间的偏移,编译器逐渐发挥作用,把越来越多的代码编译成本地代码,这样可以减少解释器的中间损耗,获得更高的执行效率。
当程序运行环境中内存资源限制较大,可以使用解释执行节约内存,反之可以使用编译执行来提升效率。
我会持续更新关于物联网、云原生以及数字科技方面的文章,用简单的语言描述复杂的技术,也会偶尔发表一下对IT产业的看法,欢迎大家关注、转发和评论。