首页 > 其他分享 >JVM解释器与JIT编译器如何并存?

JVM解释器与JIT编译器如何并存?

时间:2023-02-18 12:37:26浏览次数:38  
标签:解释器 虚拟机 JIT 编译 编译器 JVM 执行


[1] JVM解释器

  JVM设计的初衷仅仅只是为了满足Java程序实现跨平台特性,因此避免采用静态编译的方式直接生成本地机器指令,从而诞生了实现解释器在运行时采用逐行解释字节码的执行程序。

  解释器真正意义上所承担的角色就是一个运行时“翻译者”,将字节码文件中的内容“翻译”为对应平台的本地机器指令执行,当一条字节码指令被解释执行完成后,接着再根据PC寄存器中记录的下一条需要被执行的字节码指令执行解释操作。

  由于解释器在设计和实现上非常简单,因此除了Java语言之外,还有许多高级语言同样也是基于解释器执行的,比如Python、 Perl、Ruby等。

  但是在今天,基于解释器执行已经沦落为低效的代名词,但无论如何基于解释器的执行模式仍然为中间语言的发展做出了不可磨灭的贡献。

[2] JIT编译器

  Java中三个编译器:

  Java 语言的“编译器” 其实是一段“不确定”的操作过程,因为它可能是指一个前端编译器(其实叫“编译器的前端” 更准确一些)把.java文件转变成.class文件的过程。

  也可能是指虚拟机的后端运行期编译器(JIT 编译器,Just In Time Compiler)把字节码转变成机器码的过程。

  还可能是指使用静态提前编译器(AOT 编译器,Ahead Of Time Compiler)直接把.java文件编译成本地机器代码的过程。

  Java中三个编译名词:

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

  2、JIT编译(just-in-time compilation)狭义来说是当某段代码即将第一次被执行时进行编译,因而叫“即时编译”。JIT编译是动态编译的一种特例。JIT编译一词后来被泛化,时常与动态编译等价;但要注意广义与狭义的JIT编译所指的区别。

  3、自适应动态编译(adaptive dynamic compilation)也是一种动态编译,但它通常执行的时机比JIT编译迟,先让程序“以某种式”先运行起来,收集一些信息之后再做动态编译。这样的编译可以更加优化。

  JIT编译器:

  在部分商用虚拟机中(如HotSpot),Java程序最初是通过解释器(Interpreter)进行解释执行的,当虚拟机发现某个方法或代码块的运行特别频繁时,就会把这些代码认定为“热点代码”。

  为了提高热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化,完成这个任务的编译器称为即时编译器(Just In Time Compiler)。

  即时编译器并不是虚拟机必须的部分,Java虚拟机规范并没有规定Java虚拟机内必须要有即时编译器存在,更没有限定或指导即时编译器应该如何去实现。

  但是,即时编译器编译性能的好坏、代码优化程度的高低却是衡量一款商用虚拟机优秀与否的最关键的指标之一,它也是虚拟机中最核心且最能体现虚拟机技术水平的部分。

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

[3] HotSpot虚拟器中解释器和编译器如何并存?

  尽管并不是所有的Java虚拟机都采用解释器与编译器并存的架构,但许多主流的商用虚拟机(如HotSpot),都同时包含解释器和编译器。

  解释器与编译器两者各有优势:当程序需要迅速启动和执行的时候,解释器可以首先发挥作用,省去编译的时间,立即执行。

  在程序运行后,随着时间的推移,编译器逐渐发挥作用,把越来越多的代码编译成本地代码之后,可以获取更高的执行效率。

  当程序运行环境中内存资源限制较大(如部分嵌入式系统中),可以使用解释器执行节约内存,反之可以使用编译执行来提升效率。此外,如果编译后出现“罕见陷阱”,可以通过逆优化退回到解释执行。

  解释器的执行,抽象的看是这样的:输入的代码 -> [ 解释器 解释执行 ] -> 执行结果

  而要JIT编译然后再执行的话,抽象的看则是:输入的代码 -> [ 编译器 编译 ] -> 编译后的代码 -> [ 执行 ] -> 执行结果

  说JIT比解释快,其实说的是“执行编译后的代码”比“解释器解释执行”要快,并不是说“编译”这个动作比“解释”这个动作快。

  JIT编译再怎么快,至少也比解释执行一次略慢一些,而要得到最后的执行结果还得再经过一个“执行编译后的代码”的过程。

  所以,对“只执行一次”的代码而言,解释执行其实总是比JIT编译执行要快。怎么算是“只执行一次的代码”呢?粗略说,下面两个条件同时满足时就是严格的“只执行一次”:

  1、只被调用一次,例如类的构造器(class initializer,())

  2、没有循环

  对只执行一次的代码做JIT编译再执行,可以说是得不偿失。

  对只执行少量次数的代码,JIT编译带来的执行速度的提升也未必能抵消掉最初编译带来的开销。

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

  对一般的Java方法而言,编译后代码的大小相对于字节码的大小,膨胀比达到10x是很正常的。

  同上面说的时间开销一样,这里的空间开销也是,只有对执行频繁的代码才值得编译,如果把所有代码都编译则会显著增加代码所占空间,导致“代码爆炸”。

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


标签:解释器,虚拟机,JIT,编译,编译器,JVM,执行
From: https://blog.51cto.com/u_15942590/6065290

相关文章

  • JVM内存模型分析
    JVMJVM内存模型解析JVM内存模型:https://juejin.cn/post/7024358170642350093https://www.cnblogs.com/blknemo/p/13296007.html线程独占:JVM栈,本地方法栈,程序计数......
  • JVM中的GC系统
    什么是GC?GC(GarbageCollection)称之为垃圾回收,在JVM的执行引擎中自带这样的一个GC系统,此系统会按照一定的算法对内存进行监控和垃圾回收。如何判断哪些对象是垃圾?1、引用......
  • JVM初始化一个类包含几个步骤?
    加入这个类还没有被加载和连接,则程序先加载并连接该类加入该类的直接父类还没有初始化,则先初始化其直接父类加入类有初始化语句,则系统依次执行这些初始化语句当执行第......
  • 第一周复习笔记(MySQL、Redis、JVM、JUC)
    MySQL1.引擎1.1Innodb和MyIsAM的区别1.2Innodb的逻辑存储结构2.索引2.1索引的分类2.2索引优化2.3索引失效的场景3.事务3.1事务的隔离级别3.2ACID原则......
  • 谈JVM参数GC线程数ParallelGCThreads合理性设置
    作者:京东零售刘乐1.ParallelGCThreads参数含义在讲这个参数之前,先谈谈JVM垃圾回收(GC)算法的两个优化标的:吞吐量和停顿时长。JVM会使用特定的GC收集线程,当GC开始的时候,GC......
  • 4. JVM
    JVM的内存区域,即运行时数据区域。线程私有的:程序计数器虚拟机栈本地方法栈线程共享的:堆方法区直接内存Java内存模型(JMM),Happens-Before规则。JVM内存结构和Ja......
  • 面试必问:JVM 如何确定死亡对象?
    在JVM中,有两个非常重要的知识点,一个是JVM的内存布局(JVM运行时的数据区域),另一个就是垃圾回收。而垃圾回收中又有两个重要的知识点,一个是如何确定JVM中的垃圾对象,另一......
  • 常用的jvm调优的参数
    -Xms2g:初始化推大小为2g;-Xmx2g:堆最大内存为2g;-XX:NewRatio=4:设置年轻的和老年代的内存比例为1:4;-XX:SurvivorRatio=8:设置新生代Eden和Survivor比例为8:2;–XX:......
  • 【技术剖析】7. 看看毕昇 JDK 团队是如何解决 JVM 中 CMS 的 Crash
    【技术剖析】7.看看毕昇JDK团队是如何解决JVM中CMS的Crashhttps://bbs.huaweicloud.com/forum/thread-168485-1-1.html JDKJVM发表于2021-11-1016:24:5......
  • JVM运行时内存区
    JVM运行时内存区是如何划分的?方法区(MethodArea):存储类的字节码信息、常量池堆区(Heap Area):存储对象Java方法栈(StackArea):所有方法运行时,会创建一个栈帧对象,然后进入栈(......