执行引擎属于JVM的下层,里面包括解释器、及时编译器、垃圾回收器
![image.png](https://img-blog.csdnimg.cn/img_convert/99c5b7e136cc24a85544ef78a70cea98.png#clientId=ufde70937-b42b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=421&id=ud9b2d9c3&margin=[object Object]&name=image.png&originHeight=1182&originWidth=1842&originalType=url&ratio=1&rotation=0&showTitle=false&size=709756&status=done&style=none&taskId=uff3a2493-6ea9-42b5-8c76-c969bd13ac6&title=&width=656)
![image.png](https://img-blog.csdnimg.cn/img_convert/1baf383190bc4f699a1d84bc27ecbe4c.png#clientId=ufde70937-b42b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=250&id=u1e174682&margin=[object Object]&name=image.png&originHeight=814&originWidth=1674&originalType=url&ratio=1&rotation=0&showTitle=false&size=287306&status=done&style=none&taskId=u5920c3a0-34c3-4df7-bc15-4ddc448afcc&title=&width=514)
![image.png](https://img-blog.csdnimg.cn/img_convert/22865b122d6abfa76a17b6d3a42c0c39.png#clientId=ufde70937-b42b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=236&id=u69473074&margin=[object Object]&name=image.png&originHeight=539&originWidth=1186&originalType=binary&ratio=1&rotation=0&showTitle=false&size=230971&status=done&style=none&taskId=ue78af196-61c6-4a57-a2ca-bc63863cf99&title=&width=520)
1、前端编译:从Java程序员-字节码文件的这个过程叫前端编译
2、执行引擎这里有两种行为:一种是解释执行,一种是编译执行(这里的是后端编译)。
![image.png](https://img-blog.csdnimg.cn/img_convert/774e955b50be4a338fbc95a9ecfe84fb.png#clientId=ufde70937-b42b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=398&id=u7b663bbc&margin=[object Object]&name=image.png&originHeight=990&originWidth=1145&originalType=url&ratio=1&rotation=0&showTitle=false&size=279393&status=done&style=none&taskId=u89b0092c-3a80-455e-bf4f-2d029d4656f&title=&width=460)
从整体上来看,所有的Java虚拟机的执行引擎输入,输出都是一致的:输入的是字节码二进制流,处理过程是字节码解析执行的等效过程,输出的是执行过程
![image.png](https://img-blog.csdnimg.cn/img_convert/706ab5d0ad1f713c8b51bc8913b936fe.png#clientId=ufde70937-b42b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=260&id=ucd2448b3&margin=[object Object]&name=image.png&originHeight=730&originWidth=1538&originalType=url&ratio=1&rotation=0&showTitle=false&size=295308&status=done&style=none&taskId=uc783b8af-d297-4ef2-a0c6-03f9b169845&title=&width=548)
大部分的程序代码转换成物理机的目标代码(机器指令)或虚拟机能执行的指令集之前,都需要经过上图中的各个步骤
说明:绿色部分是解释的过程,蓝色部分是编译的过程(橙色部分是javac执行过程?)
Java代码编译是由Java源码编译器(前端编译器)来完成,流程图如下所示:
![image.png](https://img-blog.csdnimg.cn/img_convert/6d8799864c70f6e67eb26e5e37c64a2e.png#clientId=ufde70937-b42b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=159&id=ufb341e20&margin=[object Object]&name=image.png&originHeight=234&originWidth=778&originalType=url&ratio=1&rotation=0&showTitle=false&size=333220&status=done&style=none&taskId=u4228b6e1-b199-4e52-a7b1-57b98be8d00&title=&width=527)
Java字节码的执行是由JVM执行引擎(后端编译器)来完成,流程图 如下所示:
![image.png](https://img-blog.csdnimg.cn/img_convert/32f1fcc0448a135902b3848004067caf.png#clientId=ufde70937-b42b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=232&id=u8f80ddb8&margin=[object Object]&name=image.png&originHeight=364&originWidth=840&originalType=url&ratio=1&rotation=0&showTitle=false&size=358652&status=done&style=none&taskId=u46b400f3-3cbb-45a1-b6f9-3a9c3e8bad5&title=&width=536)
什么是解释器(Interpreter), 什么是JIT编译器?
为什么说Java是半编译型半解释型语言?
![image.png](https://img-blog.csdnimg.cn/img_convert/76c38dce5de736bdc3a42ef280430341.png#clientId=ufde70937-b42b-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u1874f826&margin=[object Object]&name=image.png&originHeight=1059&originWidth=1953&originalType=url&ratio=1&rotation=0&showTitle=false&size=409806&status=done&style=none&taskId=ue7b95487-9c12-4601-b458-bfe91e09eef&title=)
不同的硬件平台,各自支持的指令,是有差别的。因此每个平台所支持的指令,称之为对应平台的指令集。 如常见的
![image.png](https://img-blog.csdnimg.cn/img_convert/cb09bd23de22ce238b4aa8a5c89b9c33.png#clientId=ufde70937-b42b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=255&id=u552044d8&margin=[object Object]&name=image.png&originHeight=744&originWidth=1191&originalType=url&ratio=1&rotation=0&showTitle=false&size=161183&status=done&style=none&taskId=u091d3b4d-52c7-4316-83ed-d6829502510&title=&width=408)
高级语言也不是直接翻译成机器指令,而是翻译成汇编语言码,如下面说的C和C++
![image.png](https://img-blog.csdnimg.cn/img_convert/8f2aab95838c7085641a62d67cf2166d.png#clientId=ufde70937-b42b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=434&id=udcc68bdc&margin=[object Object]&name=image.png&originHeight=492&originWidth=488&originalType=url&ratio=1&rotation=0&showTitle=false&size=302178&status=done&style=none&taskId=u8b375ecc-f658-456e-bc45-4d4d243f6b0&title=&width=430)
![image.png](https://img-blog.csdnimg.cn/img_convert/2e90b346accd9b05e6fdf7a1603561a7.png#clientId=ufde70937-b42b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=236&id=ub61d9806&margin=[object Object]&name=image.png&originHeight=814&originWidth=1674&originalType=url&ratio=1&rotation=0&showTitle=false&size=287306&status=done&style=none&taskId=uae81a383-7e94-47cf-8991-d7e9a6b5b49&title=&width=485)
JVM设计者们的初衷仅仅只是单纯地为了满足Java程序实现跨平台特性,因此避免采用静态编译的方式直接生成本地机器指令,从而诞生了实现解释器在运行时采用逐行解释字节码执行程序的想法
![image.png](https://img-blog.csdnimg.cn/img_convert/bb8a567dccd11594a60e876bd7869b4a.png#clientId=ufde70937-b42b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=278&id=u0a792566&margin=[object Object]&name=image.png&originHeight=706&originWidth=1595&originalType=url&ratio=1&rotation=0&showTitle=false&size=246927&status=done&style=none&taskId=u94af9e2f-d1b6-4ca3-9920-262d3574dba&title=&width=627)
为什么Java源文件不直接翻译成 JVM,而是先翻译成字节码文件?
可能是因为直接翻译的代价是比较大的
在Java的发展历史里,一共有两套解释执行器,即古老的字节码解释器、现在普遍使用的模板解释器
问题来了!
有些开发人员会感觉到诧异,既然HotSpot VM中已经内置JIT编译器了,那么为什么还需要再使用解释器来“拖累”程序的执行性能呢?
当虚拟机启动的时候,解释器可以首先发挥作用,而不必等待即时编译器全部编译完成再执行,这样可以省去许多不必要的编译时间。并且随着程序运行时间的推移,即时编译器逐渐发挥作用,根据热点探测功能,将有价值的字节码编译为本地机器指令,以换取更高的程序执行效率
案例来了!
![image.png](https://img-blog.csdnimg.cn/img_convert/19170b3a3bdd24f5d8ba53bdaebee1ef.png#clientId=ufde70937-b42b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=182&id=ucafccb7c&margin=[object Object]&name=image.png&originHeight=223&originWidth=507&originalType=url&ratio=1&rotation=0&showTitle=false&size=73495&status=done&style=none&taskId=u0240c9c6-7120-4cd4-a577-fdc805792ee&title=&width=414)
public class JITTest {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
list.add("让天下没有难学的技术");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行后通过VisualVM 查看,可以看到 JIT 编译的次数
![image.png](https://img-blog.csdnimg.cn/img_convert/e991bf3835828e1b14f6a1977a5a6c10.png#clientId=ufde70937-b42b-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u9f2ee171&margin=[object Object]&name=image.png&originHeight=1376&originWidth=2386&originalType=url&ratio=1&rotation=0&showTitle=false&size=342692&status=done&style=none&taskId=ub1253c09-d993-4e4e-af03-c5b2e0cb118&title=)
或者可以通过 jconsole 指令,查看 JIT编译器的存在
![image.png](https://img-blog.csdnimg.cn/img_convert/1950a418dec10899af3f3024bc67aab2.png#clientId=ufde70937-b42b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=60&id=u6a62a323&margin=[object Object]&name=image.png&originHeight=90&originWidth=507&originalType=binary&ratio=1&rotation=0&showTitle=false&size=8862&status=done&style=none&taskId=u97a5a044-52e2-4cd7-b1db-ff3478254c4&title=&width=338)
![image.png](https://img-blog.csdnimg.cn/img_convert/85fb45de2572b71aab93561e03c32498.png#clientId=ufde70937-b42b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=404&id=u546dd73c&margin=[object Object]&name=image.png&originHeight=614&originWidth=532&originalType=binary&ratio=1&rotation=0&showTitle=false&size=112748&status=done&style=none&taskId=u6601eeec-df62-4327-a9a0-8a2d9ba2cde&title=&width=349.66668701171875)
![image.png](https://img-blog.csdnimg.cn/img_convert/0a15ea2dd372c0bd612f422f777f919f.png#clientId=ufde70937-b42b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=212&id=u540b7a2f&margin=[object Object]&name=image.png&originHeight=318&originWidth=568&originalType=binary&ratio=1&rotation=0&showTitle=false&size=72880&status=done&style=none&taskId=ucf59c773-57cb-4ea9-96a6-b5b3dc27985&title=&width=378.6666666666667)
![image.png](https://img-blog.csdnimg.cn/img_convert/fa8e59c3bbfe419627eb6b8ea908fd90.png#clientId=ufde70937-b42b-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=ua38ece51&margin=[object Object]&name=image.png&originHeight=1500&originWidth=1800&originalType=url&ratio=1&rotation=0&showTitle=false&size=403621&status=done&style=none&taskId=u5a7f2211-af51-4a54-92ec-9fffc64962a&title=)
前端编译器:Sun的Javac、Eclipse JDT中的增量式编译器(ECJ)
JIT编译器:HotSpot VM的C1、C2编译器
AOT 编译器:GNU Compiler for the Java(GCJ)、Excelsior JET
当然是否需要启动JIT编译器将字节码直接编译为对应平台的本地机器指令,则需要根据代码被调用执行频率而定。关于那些需要被编译为本地代码的字节码,也被称之为“热点代码”,JIT编译器在运行时会针对那些频繁被调用的“热点代码”做出深度优化,将其直接编译为对应平台的本地机器指令,以此提升Java程序的执行性能
-XX:CompileThreshold
来人为设定![image.png](https://img-blog.csdnimg.cn/img_convert/becfc7af1d0eec5005b6a3fe9ad6449a.png#clientId=ufde70937-b42b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=427&id=ud3c35e70&margin=[object Object]&name=image.png&originHeight=604&originWidth=742&originalType=url&ratio=1&rotation=0&showTitle=false&size=254142&status=done&style=none&taskId=u52efdc57-7621-4c0b-9827-727c778121d&title=&width=525)
![image.png](https://img-blog.csdnimg.cn/img_convert/ac688b3cab6307edcc8723aec718b951.png#clientId=ufde70937-b42b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=407&id=u6d3300a0&margin=[object Object]&name=image.png&originHeight=1252&originWidth=1607&originalType=url&ratio=1&rotation=0&showTitle=false&size=369326&status=done&style=none&taskId=udd12cae9-6297-4877-83ad-8adeb06504a&title=&width=523)
-XX:-UseCounterDecay
来关闭热度衰减,让方法计数器统计方法调用的绝对次数,这样,只要系统运行时间足够长,绝大部分方法都会被编译成本地代码-XX:CounterHalfLifeTime
参数设置半衰周期的时间,单位是秒它的作用是统计一个方法中循环体代码执行的次数,在字节码中遇到控制流向后跳转的指令称为“回边”(Back Edge)。显然,建立回边计数器统计的目的就是为了触发OSR编译
![image.png](https://img-blog.csdnimg.cn/img_convert/cca5874bd7901ac9e608def0563adfbb.png#clientId=ufde70937-b42b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=484&id=ue9773954&margin=[object Object]&name=image.png&originHeight=1426&originWidth=1607&originalType=url&ratio=1&rotation=0&showTitle=false&size=401399&status=done&style=none&taskId=u4631d4b5-7143-4897-84f2-7550599b7b8&title=&width=545)
缺省情况下HotSpot VM是采用解释器与即时编译器并存的架构,当然开发人员可以根据具体的应用场景,通过命令显式地为Java虚拟机指定在运行时到底是完全采用解释器执行,还是完全采用即时编译器执行。如下所示:
-Xint
:完全采用解释器模式执行程序;-Xcomp
:完全采用即时编译器模式执行程序。如果即时编译出现问题,解释器会介入执行-Xmixed
:采用解释器+即时编译器的混合模式共同执行程序通过命令行方式可以查看或设置当前的模式
![image.png](https://img-blog.csdnimg.cn/img_convert/7f65fac1a6afa682d508efd57ba23e8a.png#clientId=ufde70937-b42b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=208&id=uad40d401&margin=[object Object]&name=image.png&originHeight=375&originWidth=1052&originalType=binary&ratio=1&rotation=0&showTitle=false&size=94774&status=done&style=none&taskId=u51688e3d-d90c-449a-9e8d-0e21697d3dc&title=&width=583.3333740234375)
输入 java -Xint -version
切换到纯解释器模式
![image.png](https://img-blog.csdnimg.cn/img_convert/8d21a18912aa7b94bf75bb8718d4a98f.png#clientId=ufde70937-b42b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=248&id=u9c56d489&margin=[object Object]&name=image.png&originHeight=425&originWidth=1056&originalType=binary&ratio=1&rotation=0&showTitle=false&size=166921&status=done&style=none&taskId=u481a073a-0852-4b4f-a6f9-04409341855&title=&width=616)
输入java -Xcomp -version
切换到纯即时编译器模式
![image.png](https://img-blog.csdnimg.cn/img_convert/c7f13c84602ad41857ec9cd45ba659ab.png#clientId=ufde70937-b42b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=279&id=ub5d59618&margin=[object Object]&name=image.png&originHeight=536&originWidth=1093&originalType=binary&ratio=1&rotation=0&showTitle=false&size=240433&status=done&style=none&taskId=uefc45305-313e-47a7-9ad8-d45bee36b14&title=&width=569.6666870117188)
输入java -Xmixed -version
切换到混合模式
![image.png](https://img-blog.csdnimg.cn/img_convert/9a732fe241819ff0b743d7c8967e457e.png#clientId=ufde70937-b42b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=406&id=u98798706&margin=[object Object]&name=image.png&originHeight=984&originWidth=1432&originalType=url&ratio=1&rotation=0&showTitle=false&size=230207&status=done&style=none&taskId=uca229b1e-6444-44d4-a0ef-eed8f70fa36&title=&width=591)
也可以通过代码的方式设置模式
/**
* 测试解释器模式和JIT编译模式
* -Xint : 5926ms
* -Xcomp : 761ms
* -Xmixed : 730ms
*/
public class IntCompTest {
public static void main(String[] args) {
long start = System.currentTimeMillis();
testPrimeNumber(1000000);
long end = System.currentTimeMillis();
System.out.println("花费的时间为:" + (end - start));
}
public static void testPrimeNumber(int count) {
for (int i = 0; i < count; i++) {
//计算100以内的质数
label:
for (int j = 2; j <= 100; j++) {
for (int k = 2; k <= Math.sqrt(j); k++) {
if (j % k == 0) {
continue label;
}
}
//System.out.println(j);
}
}
}
}
设置JVM参数,切换到纯解释器模式-Xint
运行结果为:5926ms
![image.png](https://img-blog.csdnimg.cn/img_convert/cae2df13536daf4dcb4964c088269955.png#clientId=ufde70937-b42b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=22&id=u692ac4f9&margin=[object Object]&name=image.png&originHeight=33&originWidth=321&originalType=binary&ratio=1&rotation=0&showTitle=false&size=1306&status=done&style=none&taskId=u897740c7-7971-4109-91fa-9d9b34272b0&title=&width=214)
关于混合模式的说明
![image.png](https://img-blog.csdnimg.cn/img_convert/d0ab0c09f476ea548ea82a0c4cd10ec5.png#clientId=ufde70937-b42b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=244&id=u4680678c&margin=[object Object]&name=image.png&originHeight=882&originWidth=1794&originalType=url&ratio=1&rotation=0&showTitle=false&size=147095&status=done&style=none&taskId=u50cc661f-f361-432d-a8f4-d2251a96bdb&title=&width=497)
在HotSpot VM中内嵌有两个JIT编译器,分别为Client Compiler 和 Server Compiler,但大多数情况下我们简称为 C1编译器 和 C2编译器。开发人员可以通过如下命令显式指定Java虚拟机在运行时到底使用哪一种即时编译器,如下所示:
-client
:指定Java虚拟机运行在Client模式下,并使用C1编译器
-server
:指定Java虚拟机运行在server模式下,并使用C2编译器
分层编译(Tiered Compilation)策略:程序解释执行(不开启性能监控)可以触发C1编译,将字节码编译成机器码,可以进行简单优化,也可以加上性能监控,C2编译会根据性能监控信息进行激进优化
不过在Java7版本之后,一旦开发人员在程序中显式指定命令“-server"时,默认将会开启分层编译策略,由C1编译器和C2编译器相互协作共同来执行编译任务
-XX:+UnlockExperimentalvMOptions
-XX:+UseJVMCICompiler
去激活才能使用