Java解释执行过程:

  代码装入-代码校验-代码执行

Java字节码的执行方式分为两种:即使编译方式和解释执行方式。即时编译是值解释器先将字节码编译成机器码,然后执行该机器码。解释执行的方式是指解释器通过每次解释并且执行一小段代码来完成java字节码程序的所有操作。通常采用的是解释执行的方式。

什么是JIT

1、动态编译(dynamic compilation)指的是“在运行时进行编译”;与之相对的是事前编译(ahead-of-time compilation,简称AOT),也叫静态编译(static compilation)。

2、JIT编译(just-in-time compilation)狭义来说是当某段代码即将第一次被执行时进行编译,因而叫“即时编译”。JIT编译是动态编译的一种特例。JIT编译一词后来被泛化,时常与动态编译等价;但要注意广义与狭义的JIT编译所指的区别。
3、自适应动态编译(adaptive dynamic compilation)也是一种动态编译,但它通常执行的时机比JIT编译迟,先让程序“以某种式”先运行起来,收集一些信息之后再做动态编译。这样的编译可以更加优化。

概述

JVM运行原理

在部分商用虚拟机中(如HotSpot),Java程序最初是通过解释器(Interpreter)进行解释执行的,当虚拟机发现某个方法或代码块的运行特别频繁时,就会把这些代码认定为“热点代码”。为了提高热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化,完成这个任务的编译器称为即时编译器(Just
In Time Compiler,下文统称JIT编译器)。

即时编译器并不是虚拟机必须的部分,Java虚拟机规范并没有规定Java虚拟机内必
须要有即时编译器存在,更没有限定或指导即时编译器应该如何去实现。但是,即时编译器编译性能的好坏、代码优化程度的高低却是衡量一款商用虚拟机优秀与否
的最关键的指标之一,它也是虚拟机中最核心且最能体现虚拟机技术水平的部分。

由于Java虚拟机规范并没有具体的约束规则去限制即使编译器应该如何实现,所以这部分功能完全是与虚拟机具体实现相关的内容,如无特殊说明,我们提到的编译器、即时编译器都是指Hotspot虚拟机内的即时编译器,虚拟机也是特指HotSpot虚拟机。

为什么HotSpot虚拟机要使用解释器与编译器并存的架构?

尽管并不是所有的Java虚拟机都采用解释器与编译器并存的架构,但许多主流的商用虚拟机(如HotSpot),都同时包含解释器和编译器。解释器与编译器两者各有优势:当程序需要迅速启动和执行的时候,解释器可以首先发挥作用,省去编译的时间,立即执行。在程序运行后,随着时间的推移,编译器逐渐发挥作用,把越来越多的代码编译成本地代码之后,可以获取更高的执行效率。当程序运行环境中内存资源限制较大(如部分嵌入式系统中),可以使用解释器执行节约内存,反之可以使用编译执行来提升效率。此外,如果编译后出现“罕见陷阱”,可以通过逆优化退回到解释执行。

编译的时间开销

解释器的执行,抽象的看是这样的:
输入的代码 -> [ 解释器 解释执行 ] -> 执行结果
而要JIT编译然后再执行的话,抽象的看则是:
输入的代码 -> [ 编译器 编译 ] -> 编译后的代码 -> [ 执行 ] -> 执行结果
说JIT比解释快,其实说的是“执行编译后的代码”比“解释器解释执行”要快,并不是说“编译”这个动作比“解释”这个动作快。
JIT编译再怎么快,至少也比解释执行一次略慢一些,而要得到最后的执行结果还得再经过一个“执行编译后的代码”的过程。
所以,对“只执行一次”的代码而言,解释执行其实总是比JIT编译执行要快。
怎么算是“只执行一次的代码”呢?粗略说,下面两个条件同时满足时就是严格的“只执行一次”
1、只被调用一次,例如类的构造器(class initializer,<clinit>())
2、没有循环
对只执行一次的代码做JIT编译再执行,可以说是得不偿失。
对只执行少量次数的代码,JIT编译带来的执行速度的提升也未必能抵消掉最初编译带来的开销。

只有对频繁执行的代码,JIT编译才能保证有正面的收益。

编译的空间开销

对一般的Java方法而言,编译后代码的大小相对于字节码的大小,膨胀比达到10x是很正常的。同上面说的时间开销一样,这里的空间开销也是,只有对执行频繁的代码才值得编译,如果把所有代码都编译则会显著增加代码所占空间,导致“代码爆炸”。

这也就解释了为什么有些JVM会选择不总是做JIT编译,而是选择用解释器+JIT编译器的混合执行引擎。

为何HotSpot虚拟机要实现两个不同的即时编译器?

HotSpot虚拟机中内置了两个即时编译器:Client
Complier和Server
Complier,简称为C1、C2编译器,分别用在客户端和服务端。目前主流的HotSpot虚拟机中默认是采用解释器与其中一个编译器直接配合的方式
工作。程序使用哪个编译器,取决于虚拟机运行的模式。HotSpot虚拟机会根据自身版本与宿主机器的硬件性能自动选择运行模式,用户也可以使用
“-client”或“-server”参数去强制指定虚拟机运行在Client模式或Server模式。

用Client Complier获取更高的编译速度,用Server Complier 来获取更好的编译质量。为什么提供多个即时编译器与为什么提供多个垃圾收集器类似,都是为了适应不同的应用场景。

哪些程序代码会被编译为本地代码?如何编译为本地代码?

程序中的代码只有是热点代码时,才会编译为本地代码,那么什么是热点代码呢?

运行过程中会被即时编译器编译的“热点代码”有两类:
1、被多次调用的方法。

2、被多次执行的循环体。

两种情况,编译器都是以整个方法作为编译对象。 这种编译方法因为编译发生在方法执行过程之中,因此形象的称之为栈上替换(On Stack Replacement,OSR),即方法栈帧还在栈上,方法就被替换了。

如何判断方法或一段代码或是不是热点代码呢?

要知道方法或一段代码是不是热点代码,是不是需要触发即时编译,需要进行Hot Spot Detection(热点探测)。

目前主要的热点探测方式有以下两种:
(1)基于采样的热点探测
采用这种方法的虚拟机会周期性地检查各个线程的栈顶,如果发现某些方法经常出现在栈顶,那这个方法就是“热点方法”。这种探测方法的好处是实现简单高效,
还可以很容易地获取方法调用关系(将调用堆栈展开即可),缺点是很难精确地确认一个方法的热度,容易因为受到线程阻塞或别的外界因素的影响而扰乱热点探
测。
(2)基于计数器的热点探测

采用这种方法的虚拟机会为每个方法(甚至是代码块)建立计数器,统计方法的执行次数,
如果执行次数超过一定的阀值,就认为它是“热点方法”。这种统计方法实现复杂一些,需要为每个方法建立并维护计数器,而且不能直接获取到方法的调用关系,
但是它的统计结果相对更加精确严谨。

HotSpot虚拟机中使用的是哪钟热点检测方式呢?

在HotSpot虚拟机中使用的是第二种——基于计数器的热点探测方法,因此它为每个方法准备了两个计数器:方法调用计数器回边计数器。在确定虚拟机运行参数的前提下,这两个计数器都有一个确定的阈值,当计数器超过阈值溢出了,就会触发JIT编译。

方法调用计数器

顾名思义,这个计数器用于统计方法被调用的次数。
当一个方法被调用时,会先检查该方法是否存在被JIT编译过的版本,如果存在,则优先使用编译后的本地代码来执行。如果不存在已被编译过的版本,则将此方
法的调用计数器值加1,然后判断方法调用计数器与回边计数器值之和是否超过方法调用计数器的阈值。如果超过阈值,那么将会向即时编译器提交一个该方法的代
码编译请求。
如果不做任何设置,执行引擎并不会同步等待编译请求完成,而是继续进行解释器按照解释方式执行字节码,直到提交的请求被编译器编译完成。当编译工作完成之后,这个方法的调用入口地址就会系统自动改写成新的,下一次调用该方法时就会使用已编译的版本。

回边计数器

它的作用就是统计一个方法中循环体代码执行的次数,在字节码中遇到控制流向后跳转的指令称为“回边”。

如何编译为本地代码?

Server Compiler和Client Compiler两个编译器的编译过程是不一样的。

对Client Compiler来说,它是一个简单快速的编译器,主要关注点在于局部优化,而放弃许多耗时较长的全局优化手段。

而Server Compiler则是专门面向服务器端的,并为服务端的性能配置特别调整过的编译器,是一个充分优化过的高级编译器。

参考

《深入理解Java虚拟机》

http://blog.csdn.net/u010412719/article/details/47008717

https://zhuanlan.zhihu.com/p/19977592

http://www.zhihu.com/question/37389356/answer/73820511

JVM即时编译(JIT)的更多相关文章

  1. 即时编译(JIT)

    即时编译(JIT : just-in-time compilation): 指计算机领域里,即时编译也被成为动态翻译,是一种通过在运行时将字节码翻译为机器码,从而改善字节码编译语言性能的技术 即时编译 ...

  2. Java 面试-即时编译( JIT )

    当我们在写代码时,一个方法内部的行数自然是越少越好,这样逻辑清晰.方便阅读,其实好处远不止如此,通过即时编译,甚至可以提高执行时的性能,今天就让我们好好来了解一下其中的原理. 简介 当 JVM 的初始 ...

  3. java 笔记(1)-—— JVM基础,内存数据,内存释放,垃圾回收,即时编译技术JIT,高精度类型

    1.java中5个存放数据的地方: (1).寄存器(Registers):位于CPU内部,是速度最快的存储区,但是数量和容量有限.在java中不能直接操作寄存器. (2).栈(Stack):栈位于通用 ...

  4. JIT——即时编译的原理

     介绍 java 作为静态语言十分特殊,他需要编译,但并不是在执行之前就编译为本地机器码. 所以,在谈到 java的编译机制的时候,其实应该按时期,分为两个部分.一个是 javac指令 将java源码 ...

  5. JVM实用参数(二)参数分类和即时(JIT)编译器诊断

    JVM实用参数(二)参数分类和即时(JIT)编译器诊断 作者: PATRICK PESCHLOW     原文地址    译者:赵峰 校对:许巧辉 在这个系列的第二部分,我来介绍一下HotSpot J ...

  6. 转:什么是即时编译(JIT)!?OpenJDK HotSpot VM剖析

    重点 应用程序可以选择一个适当的即时编译器来进行接近机器级的性能优化. 分层编译由五层编译构成. 分层编译提供了极好的启动性能,并指导编译的下一层编译器提供高性能优化. 提供即时编译相关诊断信息的JV ...

  7. JIT(Just in time,即时编译,边运行边编译)、AOT(Ahead Of Time,运行前编译),是两种程序的编译方式

    JIT(Just in time,即时编译,边运行边编译).AOT(Ahead Of Time,运行前编译),是两种程序的编译方式

  8. 浅析 JIT 即时编译技术

    即时编译回顾 HotSpot 虚拟机执行 Java 程序时,先通过解释器对代码解释执行,发现某个方法或代码块执行比较频繁后,对热点代码进行编译,编译后生成与本地平台相关的机器码,再去执行机器码获得较高 ...

  9. JVM-即时编译JIT

    编译简介 在谈到JIT前,还是需要对编译过程有一些简单的了解. 在编译原理中,把源代码翻译成机器指令,一般要经过以下几个重要步骤: 什么是JIT1.动态编译(dynamic compilation)指 ...

随机推荐

  1. [51nod]1678 lyk与gcd(莫比乌斯反演)

    题面 传送门 题解 和这题差不多 //minamoto #include<bits/stdc++.h> #define R register #define pb push_back #d ...

  2. spring 嵌套事务问题

    嵌套事物总结 事物成功总结 1.内外都无try Catch的时候,外部异常,全部回滚. 2.内外都无try Catch的时候,内部异常,全部回滚. 3.外部有try Catch时候,内部异常,全部回滚 ...

  3. 洛谷 P3437 [POI2006]TET-Tetris 3D

    二维线段树区间更新啊 树套树的外层树,如果是线段树的话一般似乎不能打标记?(毕竟标记不好下传) 然而起码对于这题是可以的...对于外层线段树,每个节点放两个内层线段树dat和setv,分别是得到的值和 ...

  4. 全面学习ORACLE Scheduler特性(2)管理jobs

    1.2  管理Jobs 1.2.1  启用Jobs 前面创建JOB时,由于未显式的指定ENABLED参数,因此即使指定了START_DATE,不过默认情况下JOB不会自动执行.对于这种情况,DBMS_ ...

  5. 简单的win7-cmd命令提示符

    在win7打开cmd窗口 有两个路径:(1)开始 -->所有程序 --> 附件 --> 命令提示 (2)开始 -->在搜索框输入 “cmd”   指令 作用 对文件夹的操作   ...

  6. MySQL与Sqlserver数据获取

    由于项目要求,一个.net mvc登录注册的东西网站必须放弃sqlserver数据去使用MySQL数据库,因此我遇到了一些问题,并找出相应的解决方法, 因为sqlserver跟MySQL的数据引擎不同 ...

  7. Java学习笔记-eclipse配置

    一.配置Java环境变量 JAVA_HOME:D:\Program Files\Java\jdk1.7.0_76 CLASSPATH:.;%JAVA_HOME%\lib\dt.jar;%JAVA_HO ...

  8. PHP安装yaf在ubuntu下面的问题解决

    1.在执行make的时候出现如下错误: In file included from /root/yaf-2.1.2/yaf_router.c:28: /usr/include/php/ext/pcre ...

  9. 【PostgreSQL-9.6.3】物化视图

    PostgreSQL 9.3 以后出现了物化视图.物化视图是由实实在在的数据组成,这是和一般视图的根本区别. 1. 物化视图创建语法如下: --创建语法 CREATE MATERIALIZED VIE ...

  10. VC++函数只被调用一次

    如何保证某个函数只被调用一次   一个函数caller会在其内部调用另外一个函数callee,现在的情况是,caller可能会在多个地方被多次调用,而你希望callee只在第一次被调用时被调用一次.一 ...