从知乎扣出来的内容 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. 使用存储过程非常慢,但是直接执行SQL很快

    使用存储过程非常慢,但是直接执行SQL很快. 了解基本情况后,初步判断是参数嗅探问题 在对应的SQL语句后面使用 OPTION(OPTIMIZE FOR UNKNOWN) 参考了 http://www ...

  2. 关于Java Logger类的使用问题 - 内存不释放

    原文地址:http://www.ihuxu.com/p/236.html   说明:这是一个利用Java Logger类的示例,完成简单的日志记录功能.代码中有部分类库没有说明,但不影响阅读. 本人技 ...

  3. Tomcat服务器启动失败:Could not publish server configuration for Tomcat v8.0 Server at localhost. Multiple Contexts have a path of

    在eclipse中使用Tomcat8.0时,出现了如下错误: 解决办法: 在建立Tomcat服务时,eclipse会自动生成一个Servers的项目. 在这个项目中,找到你部署项目的服务文件夹. 在这 ...

  4. Introduction of Build Tool/Maven, Gradle

    什么是build tool: build tool是可以自动由源代码创建可执行的应用程序的程序. Building 包括编译.链接和打包代码成一个可用的或可执行形式. 在小型项目,开发人员常常会手动调 ...

  5. OC基础知识总结 分类: ios学习 OC 2015-06-26 17:58 58人阅读 评论(0) 收藏

    //OC: Objective-C, 面向对象的C语言 //OC与C的区别 //1.OC是C的超集, C语言的所有语法都可以在OC中使用 //2.OC是面向对象 //3.OC是一门运行时语言 //4. ...

  6. 1.1.4 A+B for Input-Output Practice (V)

    A+B for Input-Output Practice (V) Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K ...

  7. Linux文件共享(单进程之间、多进程之间)

    转载:https://www.cnblogs.com/frank-yxs/p/5925603.html 在同一个进程中,实现文件共享的方法有两种: 多次使用open函数打开相同文件 使用dup/dup ...

  8. QT4 交叉编译

    配置 ./configure \-prefix /usr \-no-rpath \-embedded arm \-xplatform /qws/linux-arm-gnueabi-g++ \-dept ...

  9. SVM核技巧之终极分析

    参考文献: http://www.blogjava.net/zhenandaci/archive/2009/03/01/257237.html http://www.cnblogs.com/jerry ...

  10. NASSA’s Robot

    NASSA的机器人降落到了火星,降落的地方可以用X-Y坐标表示.机器人最开始在(0, 0).由于传输问题,部分指令可能会混淆,现在给出确定的命令与未知命令,请帮忙确认机器人的X.Y坐标最小最大值分别是 ...