从知乎扣出来的内容 https://www.zhihu.com/question/37389356

作者:RednaxelaFX
链接:https://www.zhihu.com/question/37389356/answer/73820511
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

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

1. 编译的时间开销

解释器的执行,抽象的看是这样的:

输入的代码 -> [ 解释器 解释执行 ] -> 执行结果

而要JIT编译然后再执行的话,抽象的看则是:

输入的代码 -> [ 编译器 编译 ] -> 编译后的代码 -> [ 执行 ] -> 执行结果

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

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

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

  • 只被调用一次,例如类的初始化器(class initializer,<clinit>()V)
  • 没有循环

对只执行一次的代码做JIT编译再执行,可以说是得不偿失。
对只执行少量次数的代码,JIT编译带来的执行速度的提升也未必能抵消掉最初编译带来的开销。
只有对频繁执行的代码,JIT编译才能保证有正面的收益。

况且,并不是说JIT编译了的代码就一定会比解释执行快。切不可盲目认为有了JIT就可以鄙视解释器了,还是得看实现细节如何。
有个很经典的例子:LuaJIT 2里有一个实现得非常优化的解释器,它解释执行的速度甚至比LuaJIT 1的JIT编译后的代码的速度还要快。

2. 编译的空间开销

举个最简单的例子:

  public static int foo() {
return 42;
}

其字节码大小只有3字节:

  public static int foo();
Code:
stack=1, locals=0, args_size=0
0: bipush 42
2: ireturn

而由Linux/x86-64上的HotSpot VM的Server Compiler将其编译为机器码后,则膨胀到了56字节:

  # {method} 'foo' '()I' in 'XX'
# [sp+0x20] (sp of caller)
0x00000001017b8200: sub $0x18,%rsp
0x00000001017b8207: mov %rbp,0x10(%rsp) ;*synchronization entry
; - XX::foo@-1 (line 3)
0x00000001017b820c: mov $0x2a,%eax
0x00000001017b8211: add $0x10,%rsp
0x00000001017b8215: pop %rbp
0x00000001017b8216: test %eax,-0x146021c(%rip) # 0x0000000100358000
; {poll_return}
0x00000001017b821c: retq
0x00000001017b821d: hlt
0x00000001017b821e: hlt
0x00000001017b821f: hlt
[Exception Handler]
[Stub Code]
0x00000001017b8220: jmpq 0x00000001017b50a0 ; {no_reloc}
[Deopt Handler Code]
0x00000001017b8225: callq 0x00000001017b822a
0x00000001017b822a: subq $0x5,(%rsp)
0x00000001017b822f: jmpq 0x000000010178eb00 ; {runtime_call}
0x00000001017b8234: hlt
0x00000001017b8235: hlt
0x00000001017b8236: hlt
0x00000001017b8237: hlt

对一般的Java方法而言,编译后代码的大小相对于字节码的大小,膨胀比达到10x是很正常的。上面的例子比较极端一些,但还是很能反映现实状况的。

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

3. 编译时机对优化的影响

有些JIT编译器非常简单,基本上不做啥优化,也倒也没啥影响。
但现代做优化的JIT编译器都非常注重使用profile信息,而profile是需要通过执行用户程序来获取的。
这样,编译得太早的话,就来不及收集足够profile信息,进而会影响优化的效果;而编译太迟的话,即便收集了很多高质量的profile,但却也已经付出了profile的额外开销,编译出来的代码再快或许也弥补不过来了。

在解释器里实现收集profile的功能,等解释执行一段时间后再触发JIT编译,这样就可以很好的平衡收集profile与编译优化这两方面。

当然,收集profile也可以在JIT编译器里做:一开始先JIT编译生成收集profile的版本的代码,等收集了到足够profile后触发重新编译,再生成出优化的、不带profile的版本。JRockit基本上就是这样做的。这方面在本回答开头放的链接里已有说明。

为什么 JVM 不用 JIT 全程编译的更多相关文章

  1. 为什么 JVM 不用 JIT 全程编译?

    考虑到跨平台,所以无法使用AOT: 考虑到执行效率,所以无法全部使用JIT: 编译技术大约分为两种,一种AOT,只线下(offline)就将源代码编译成目标机器码,这是普遍用在系统程序语言中:另一种是 ...

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

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

  3. JVM的JIT机制

    因为 JVM 的 JIT 机制的存在,如果某个函数被调用多次之后,JVM 会尝试将其编译成为机器码从而提高执行速度.

  4. 小师妹学JVM之:JIT中的LogCompilation

    目录 简介 LogCompilation简介 LogCompilation的使用 解析LogCompilation文件 总结 简介 我们知道在JVM中为了加快编译速度,引入了JIT即时编译的功能.那么 ...

  5. How would you differentiate JDK, JRE, JVM, and JIT?

    Q5. How would you differentiate JDK, JRE, JVM, and JIT?A5. There is no better way to get the big pic ...

  6. 浅析 JIT 即时编译技术

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

  7. 小师妹学JVM之:深入理解JIT和编译优化-你看不懂系列

    目录 简介 JIT编译器 Tiered Compilation分层编译 OSR(On-Stack Replacement) Deoptimization 常见的编译优化举例 Inlining内联 Br ...

  8. JVM JIT动态编译

    一.概述 1.1 基本概念 a. 动态编译(dynamic compilation)指的是"在运行时进行编译":与之相对的是事前编译(ahead-of-time compilati ...

  9. JVM之JIT

    JIT技术是JVM中最重要的核心模块之一.我的课程里本来没有计划这一篇,但因为不断有朋友问起,Java到底是怎么运行的?既然Hotspot是C++写的,那Java是不是可以说运行在C++之上呢?为了澄 ...

随机推荐

  1. python 模块化管理,导入自己的模块(包)

    很多东西似懂非懂,或者当时看懂了,但是感觉不是很重要,经常不用,时间久了,也就变得似懂非懂了.今天被某度电话面试问道一个问题,就是模块倒入,其实我之前也是很仔细的研究过的,不过由于平时做的东西太简单, ...

  2. 用MyEclipse JPA创建项目(四)

    MyEclipse 3.15 Style——在线购买低至75折!火爆开抢>> [MyEclipse最新版下载] 本教程介绍了MyEclipse中的一些基于PA的功能. 阅读本教程时,了解J ...

  3. MS14-021: Internet Explorer 安全更新: 2014 年 5 月 1 日

    微软推送全平台IE零日漏洞补丁 2014.05.2 3 Comments 207 views今天,微软正式发布IE安全补丁,修复4月底曝光的零日漏洞.用户可通过Windows Update自动更新服务 ...

  4. Engineering Management

    Engineering Management 工程師面對工作或挑戰時,可應用以下法則一步一步把工作規管和實施. 1.      Planning 計劃 2.      Organization 組織 ...

  5. 对抗网络之目标检测应用:A-Fast-RCNN

    对抗网络之目标检测应用:A-Fast-RCNN 论文:A-Fast-RCNN: Hard Positive Generation via Adversary for Object Detection ...

  6. Java8函数式编程学习笔记(初探)

    编程语言的整个目的就在于操作值,要是按照历史上编程语言的传统,这些值被成为一等值,而编程语言中的其他结构也许有助于表示值的结构,但在程序执行期间不能传递,因此为二等值,比如方法和类等则是二等值,类可以 ...

  7. poj-1170 (状态压缩形式下的完全背包)

    #include <iostream> #include <algorithm> #include <cstring> using namespace std; ; ...

  8. Codeforces 1096G. Lucky Tickets【生成函数】

    LINK 题目大意 很简单自己看 思路 考虑生成函数(为啥tags里面有一个dp啊) 显然,每一个指数上是否有系数是由数集中是否有这个数决定的 有的话就是1没有就是0 然后求出这个生成函数的\(\fr ...

  9. C# [ModelName]标记 模型,类名称重复。

    前几天遇到一个不算bug的bug  记录分享一下  出错情况 webapi  程序会自带一个模板  如图 点某一个接口进去后     出错原因      model实体中出现了名称一样的(并不会影响程 ...

  10. Linux内核配置

    1.autoconf.h文件 老版本的Linux内核中,执行make menuconfig后,编译系统会把所有的配置信息保存到源码顶层目录下的.config文件中,然后将.config中的内容转换为C ...