JVM执行引擎(Execution Engine)

2021-07-05 19:05:57 浏览数 (1)

上文:JVM-直接内存(Direct Memory)

历史:

在了解执行引擎前先了解一下计算机的语言的发展历史。如下:

机器码?

机器码是通过各种二进制表示的一串可以被计算机直接运行的指令,这种也叫机器语言,通常用010110这种来表示。虽然容易被计算机理解并且执行速度快,但是与我们所有的语言相关太大,并且维护成本巨大。

指令?

由于机器码可读性实在太差了,所以在机器码的基础上又发明了,指令,这一条指令可以对应一串对应的机器码,比如mov 对应一机器码010101(这里是假设),比如纯二进制的指令可读性稍微高。

指令集

由于指令是应用于硬件基础上的,但由于随着硬件发展得非常快以及各个厂商的硬件对用的架构不一样所以所产生的指令也不一样,所以每个平台所支持的指令称为指令集。比如:

x86指令集,对应的是x86架构平台。

ARM指令集,对应的是ARM架构平台。

汇编语言

随着时间的推移,人们又发现指令集也是比较麻烦可读性还是比较差,然后又发明了汇编语言。而汇编语言最大的区别是新增了一系列的支持,比如 助记符 、地址符、标号等来代替指令或指令集,这样也就不用因为不同的产家需要维护多套指令集。

高级语言

随着技术的发展,人们又发现原来的汇编语言,不支持跨平台、跨硬件等弊端,每一个平台都得单独去搞一套汇编语言很繁琐,所以在此基础上又发明了高级语言、比如 c c java等,这样极大的增加了接近我们现有语言,可读性和研发效率空前提高,而高级语言自带翻译将代码翻译成机器能读懂的机器指令或叫机器码。

也就是说随着软硬件的发展,从机器码->指令(指令集)->汇编语言->高级语言,慢慢接近现在的语言以及大的提升了生产和工作的效率,了解了以上你才发现,原来jvm的执行引擎其实只是高级语言的一个翻译过程。

什么是jvm的执行引擎?

执行引擎是JVM的核心组成部分之一,主要用来执行Java生成.class的字节码,解析成各种cpu所能执行的二进制指令。因为JVM加载字节码相关指令后,这些字节码指令、符号表和其他辅助信息无法被操作系统直接识别运行,所以就需要执行引擎来实现。

个人理解:执行引擎就类似将中文翻译成英文或日文,而翻译的这个人可以类似这种引擎,目的是让大家听得懂。

Java 代码编译和执行过程

什么是解释器(Interpreter),什么是JIT编译器?

不管解释器或者编译器最终的结果都是为了将字节码”翻译“成机器语言。

解释器:Java虚拟机启动时,会根据预定义的规范对字节码采用逐行解释的方式执行,将每条字节码文件中的内容编译为对应平台的本地机器指令执行。(一行一行解释字节码指令,立即执行,不需要编译)

即时编译器有哪些?

后端编译器(JIT):

JIT编译器(Just In time Compiler):虚拟机将字节码直接编译成和本地机器平台相关的机器语言。(把热点代码编译成机器语言,编译慢,执行快)

前端编译器(AOT):

JRockIT VM程序:可以直接将.java文件编译成本地机器代码的过程,但是在启动的时候耗费时间比较长;

注意mac是不支持JIT的,因为mac把内存可读可写可执行中的可执行禁用掉了。

为什么有编译器还要有解释器?

因为解释器需要每一行指令都去翻译,这样效率非常低下。只能是那些一开始已经确定好的程序,当程序启动的时候,解释器可以马上使用,省去编译的时间,立即执行。而编译器,编译的时间远远大于解释器,虽然可以很好避免函数被解释执行,而是将整个函数体编译成为机器码,每次函数执行时,只执行编译后的机器码即可,这种方式可以使执行效率大幅度提升。所以各有利弊,在不同的场景可以使用。

Hotspot JVM的执行方式

jvm启动的时候非常快,这时候用的是解释器,这样的话可以减少编译的时间,且不会出现较长的卡顿,并且随着程序运行的时间推移,即时编译时发生了作用,这里候通过热点探测功能,将有价值的字节码编译为本地机器指令,以换取更高的程序执行效率。

什么是热点探测功能?

JVM会根据代码被调用的频率来判断这块代码或这行代码是否是热点代码,如果达到一定阀值则会被JIT编译器探测到,然后将其直接编译为对应平台的本地机器指令,以此提升Java程序的执行性能,这种就叫热点探测。HotSpot采用的热点探测方式是基于计数器和回边计数器来进行统计。

client模式:统计方法被调用次数超过1500次,则会促发JIT编译。

server模式:统计方法被 调用10000次,则会促发JIT编译。

以上两种都可以通过:-xx:ComplieThreadshold来设定。

热度衰减

方法计时器次数不是一成不变的,如果频率下降,当超过一定时间限度(可以设定),如果方法的调用次数还不及以将这块方法提交给编译器编译,那这个计数器就会被减少一半,这个过程也叫衰减,这个期间也叫半衰周期(有点像在上化学课啊....)。当然也可以通过-XX:-UseCounterDecay来进行关闭热度衰减 ,也可以通过--XX:ConuterHalfLifeTime来设置半衰时间。

C1和C2是什么?

C1指的是C1编译器(-client)会对字节码进行简单的和可靠的优化,耗时短。以达到更快的编译速度。主要的优化:

方法内联:将引用的函数编译到引用点处,减少栈帧的生成,减少参数传递以及跳转过程。

去虚拟化:对唯一的实现类进行内联。

冗余消除:在运行期间把一些不会的执行的代码折叠掉。

C2指的是C2编译器(-server)耗时较长的优化,以及激进优化。优化后的代码执行效率更高。

标量替换:用标量值代替聚合对象的属性值;

栈上分配:对未逃逸的对象分配对象在栈而不是堆

同步消除:消除同步操作,通常指synchronized

分层编译是什么?

系统启动的时候默认是用C1,热机后采用C2。

相关配置

-Xint :完全采用解释器模式执行程序;

-Xcomp:完全采用即时编译器模式执行程序。如果即时编译器出问题,解释器会介入执行。

-Xmixed:采用解释器 即时编译器的混合模式共同执行程序。

最后

JVM执行引擎是JVM的核心功能,主要起到翻译工作,当然有直接翻译和间接翻译(JIT),起到非常关键的作用,通过热点探测进行统计是否通过编译器进行生成系统可执行的机器码,对后续相关的对象逃逸也跟此内容关联极大。

参考书籍

《深入理解Java虚拟机》 –周志明

参考视频:

JVM从入门到精通_宋红康

https://www.bilibili.com/video/av286486680/

0 人点赞