回顾
这篇文章将学习jvm的另一个核心组成部分执行引擎。讲解之前我们先复习一下前两篇文章:
- jvm是如何运行的—从根儿上理解jvm(一)抽象-jvm-jvm位置-jvm启动-jvm体系结构。
- jvm是如何运行的—从根儿上理解jvm(二)jvm内存模型-栈帧。
通过这两篇文章,我们已经很好的理解了jvm的整体结构。接下来我们将详细讲解执行引擎。
执行引擎
我们知道,程序最终是由计算机控制器(CPU)执行的。应用程序是不直接与硬件打交道的,所有这些工作是由操作系统处理的。不同CPU厂商有不同的指令集架构(目前最流行和被我们熟知的就是X86指令集架构),操作系统会屏蔽硬件的不同,适应不同的CPU体系架构。计算机中的思想很多都是相通的,jvm也是类似的思想,屏蔽了操作系统的不同,实现跨平台。
JVM 的主要任务是负责装载字节码到其内部,但字节码并不能够直接运行在操作系统之上,因为字节码指令并非等价于本地机器指令,它内部包含的仅仅只是一些能够被 JVM 所识别的字节码指令、符号表,以及其他的辅助信息。
那么,如果想让一个 java 程序运行起来,执行引擎(Execution Engine) 的任务就是将字节码指令解释/编译为对应平台上的本地机器指令才可以。简单来说,JVM 中的执行引擎充当了将高级语言翻译为机器语言的译者。
执行引擎执行什么样的字节码,依赖于jvm中的程序计数器(方法执行过程中,执行引擎有可能会通过存储在局部变量表中的对象引用准确定位到存储在 Java 堆区中的对象实例信息)。
java执行引擎执行字节码的过程可以通过字节码解释器/JIT编译器完成(jvm厂商不同采用的执行过程也不同,如HotSpot JVM两种策略都会用到,原因我们后续会说明)。
解释器
解释器,可以理解为翻译的过程。如:字节码指令iconst_3会被翻译成操作系统可识别的指令(本地机器指令)。解释器需要执行一个解释指令的过程,因此执行效率没有C/C++的执行效率高(直接编译成机器码)
JIT编译器
JIT编译器, 就是虚拟机将源代码直接编译成和本地机器平台相关的机器语言。个人更喜欢关于JIT编译器英文的描述:
The Just-In-Time (JIT) compiler is a component of the runtime environment that improves the performance of Java™ applications by compiling bytecodes to native machine code at run time.
有兴趣的同学可以参考IBM官网上的讲解:The JIT compiler
HotSpot JVM为什么同时存在解释器和JIT编译器
曾几何时,我也一直被java执行效率的问题困扰。没有搞明白解释器真是存在的原因,认为解释器仅仅为实现了跨平台的设计和保留的。但是如果是为了实现跨平台,为什么不能把这个工作直接交给JIT编译器来做呢?这样既解决了跨平台的问题,也解决了执行效率的问题。一切都是为什么???
问题的本事就是答案,其实这个本身就不是问题,完全可以不需要解释器,也有jvm厂商这样去做了。
- JRockit VM,世界上最快的java虚拟机。JRockit内部不包含解释器实现,全部代码都靠及时编译器(JIT)编译后执行。
- HotSpot VM,是目前市面上高性能虚拟机的代表作之一。它采用解释器与及时编辑器并行的结构。在 Java 虚拟机运行时,解释器和即时编译器能够相互协作,各自取长补短,尽力去选择最合适的方式来权衡编译本地代码的时间和直接解释执行代码的时间。
当程序启动后,解释器可以马上发挥作用,省去编译的时间,立即执行。编译器要想发挥作用,把代码编译成本地代码,需要一定的执行时间。但编译为本地代码后,执行效率高。所以:尽管 JRockit VM中程序的执行性能会非常的高效,但程序在启动时必然会花费更长的时间来进行编译。对于服务器来说,启动时间并非时关注重点,但对于那些看中启动时间的应用场景而言,或许就需要采用解释器与及时编译器并存的架构来换取一个平衡点。在此模式下,当 Java 虚拟机启动时,解释器可以首先发挥作用,而不必等待即时编译器全部编译完成后在执行,这样可以省去许多不必要的编译时间。随着时间的推移,编译器发挥作用,把越来越多的代码编译成本地代码,获得更高的执行效率。
同时,解释执行在编译器进行激进优化不成立的时候,作为编译器的“逃生门”。
小结
jvm是java程序运行的基石,学好jvm对能更好的理解java程序是如何运行起来的。接下来将会继续陪跑学习jvm的垃圾回收,从根上了解jvm的垃圾回收机制。