关于Java的JIT(即时编译器)知识整理

Java JIT编译器原理与优化技术解析
本文围绕Java的JIT(即时编译器)展开,介绍了解释执行和编译执行的区别、Java代码编译过程。阐述了HotSpot热点探测技术,包括基于采样和计数器的方法。还介绍了JIT编译器分类,如C1、C2编译器及混合模式、分层编译。最后讲解了JIT编译优化技术,如逃逸分析、方法内联和退优化。

前言

之前在整理终于搞懂了!字符串拼接的各种姿势以及底层的小知识这篇文章的时候提到了锁消除,然后了解到逃逸分析,之后又去学习了相关知识,了解到Java逃逸分析只出现在JIT(即时编译器)进行。这时候随着深入了解,出现了以下几个问题。

一、JIT(即时编译器)

了解Java逃逸分析只出现在JIT(Just In Time)进行,就不禁想JIT是什么。

1.1 解释执行和编译执行的区别

解释执行:解释执行是采用匹配执行解释器(解释器是个黑盒,通常也有编译器的组成部分)内部已经编译好的机器码,不是生成新的机器码(也有说法是逐条翻译成机器码?)。 - 由于逐条翻译,程序启动快,但是执行效率不高。

编译执行:运行期间,通过将字节码编译成对应的新的机器码(会将其缓存起来,通过参数-XX:ReservedCodeCacheSize),然后执行。 - 需要先编译出新的机器指令,所以程序启动较慢,但是执行效率高(因为执行的是机器指令)。

1.2 Java代码编译过程

在讲JIT之前,简单过一遍刚开始时,Java执行的过程

Java将源代码翻译为字节码 -> JVM逐条解释为机器码 ->  执行  -> 执行结果

后来因为解释执行必然比执行编译好的机器指令的执行效率低,所以引入JIT(即时编译器)。在执行时,JIT会把翻译过的机器码保存起来,已备下次使用,因此从理论上来说,采用JIT技术能够在执行效率上,接近曾经纯编译技术。

1.3 JIT是什么

JIT编译器(just in time 即时编译器),当虚拟机发现某个方法或代码块运行特别频繁时,就会把这些代码认定为(Hot Spot Code 热点代码,为了提高热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各层次的优化。

那么,虚拟机是怎么知道哪些是热点代码或者运行频繁呢,那就要说说HotSpot了。

二、HotSpot是什么

现在主流的商用虚拟机(HotSpot(Oracle)、J9 VM(IBM))中几乎都同时包含``解释器和编译器。
二者在其中各有优势:当程序需要迅速启动和执行时,解释器可以首先发挥作用,省去编译的时间,立即执行;当程序运行后,随着时间的推移,编译器逐渐会返回作用,把越来越多的代码编译成本地代码后,可以获取更高的执行效率。解释执行可以节约内存,而编译执行可以提升效率。

2.1 说JIT比解释快,其实说的是“执行编译后的代码”比“解释器解释执行”要快

并不是说“编译”这个动作比“解释”这个动作快。JIT编译再怎么快,至少也比解释执行一次略慢一些,而要得到最后的执行结果还得再经过一个“执行编译后的代码”的过程。所以,对“只执行一次”的代码而言,解释执行其实总是比JIT编译执行要快。

怎么算是“只执行一次的代码”呢?粗略说,下面两个条件同时满足时就是严格的“只执行一次”

  1. 只被调用一次,例如类的构造器(class initializer,())
  2. 没有循环
2.2 对于为何JIT要只对“热点代码”进行即使编译

大概分为两个情况考虑

  1. 时间
    对只执行一次的代码做JIT编译再执行,可以说是得不偿失。对只执行少量次数的代码,JIT编译带来的执行速度的提升也未必能抵消掉最初编译带来的开销。只有对频繁执行的代码,JIT编译才能保证有正面的收益。
  2. 空间
    对一般的Java方法而言,编译后代码的大小相对于字节码的大小,膨胀比达到10x是很正常的。同上面说的时间开销一样,这里的空间开销也是,只有对执行频繁的代码才值得编译,如果把所有代码都编译则会显著增加代码所占空间,导致“代码爆炸”。这也就解释了为什么有些JVM会选择不总是做JIT编译,而是选择用解释器+JIT编译器的混合执行引擎。
2.3 热点探测技术(Hot Spot Detection)

程序中的代码只有是热点代码时,才会编译为本地代码,那么什么是热点代码呢?运行过程中会被即时编译器编译的“热点代码”有两类:

  1. 被多次调用的方法。
  2. 被多次执行的循环体

热点探测技术主要有以下两种:

2.3.1 基于采样的热点探测

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

2.3.2 基于计数器的热点探测

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

在这个方法下,JVM会为每个方法或者代码块保留一个调用计数,以预定义的编译阈值(-XX:CompileThreshold,client模式下默认是1500,server模式默认是10000)开始,
这里听到两种情况,一种是计数器用减的,到0即时编译,一种是开始0,执行一次加一,超过阈值即时编译。(结合之后的热点衰减,我这里就先认为是加的了)

两种情况,编译器都是以整个方法作为编译对象。 这种编译方法因为编译发生在方法执行过程之中,因此形象的称之

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值