Java虚拟机--JIT编译器

1.什么是JIT编译器

  JIT编译器,即Just-In-Time Compiler(即时编译器)。JIT编译属于动态编译(即运行时编译)的一种,与之对应的是静态编译(AOT)。

2.为什么要用JIT编译器

  我们都知道,通常通过javac将程序源代码编译(前端编译,与语言有关,机器无关)成字节码,JVM通过解释字节码将其翻译成对应的机器指令,逐条读入,逐条解释翻译。很显然,经过解释执行,其执行速度必然会比可执行的二进制字节码程序慢很多,这就是传统的解释器(Interpreter)的功能。为了解决这种效率问题,引入了JIT技术。

3.JIT编译器是如何工作的

              

                         JIT 工 作 原 理 图

  Java程序还是通过解释器进行解释执行,当JVM发现某个方法或代码块运行特别频繁的时候,就会认为这是“热点代码”(Hot Spot Code),然后JIT会把部分“热点代码”翻译成本地机器相关的机器码,并进行优化,然后把翻译后的机器码缓存起来,下次可以直接使用。

  HotSpot虚拟机中内置两个JIT编译器:Client CompilerServer Compiler,分别用在客户端和服务端,目前主流的HotSpot虚拟机默认是采用解释器和其中一个编译器直接配合的工作方式

  当JVM执行代码时,它并不会立即编译代码,原因有两点

    ■ 如果这段代码本身只会被执行一次,那从本质上看,编译器就是在浪费精力,因为将代码翻译成Java字节码(解释器解释执行)相对于编译这段代码并执行代码来说,要快很多。

    ■ 最优化,当JVM执行某一方法或执行遍历的次数越多,就会更了解代码结构,那么JVM在编译代码时就做出了相应的优化。

  我们可以看自己机器上安装的JDK中JIT是哪种模式:

                

  其中,无论是Clent还是Server,都是mixed mode,即解释器和编译器搭配使用的混合模式。

  注:Client启用的是代号为C1的编译器,是轻量级编译器;Server启用的是代号是C2的编译器,时重量级编译器。

4.热点检测

  想要触发JIT编译,就要先识别出热点代码,目前主要的热点代码识别方式是热点探测,有两种方式:

   ■ 基于采样的方式探测:周期性检测各个线程的栈顶,发现某个方法经常出现在栈顶,就认为是热点方法。好处是简单,缺点是无法精确确认一个方法的热度,容易受线程阻塞等原因的干扰。

   ■ 基于计数器的热点探测:采用这种方法的虚拟机会为每个方法,甚至代码块建立计数器,统计方法的执行次数,某个方法超过阀值就认为是热点方法,触发JIT编译。

  在HotSpot虚拟机中使用的是基于计数器的热点探测,它为每个方法准备了两个计数器:方法调用计数器和回边计数器。

      ■ 方法调用计数器:记录一个方法被调用次数。

      ■ 回边计数器:记录方法中的for或while的运行次数的计数器。 

5.所有对象都分配到堆上吗?

  在《深入理解Java虚拟机》中有这样一段话:“随着JIT编译器的发展和逃逸分析技术的逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化,所有的对象分配到堆上也渐渐不那么绝对了”。这是由于在编译期间,JIT会对代码做很多优化,其中有一部分优化的目的就是减少内存堆分配的压力,其中一项重要的技术叫做逃逸分析

  逃逸分析是目前Java虚拟机中比较前言的优化技术,这是一种可以有效减少Java程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法。通过逃逸分析,HotSpot编译器能够分析出一个新的对象的引用的使用范围,从而决定是否要将这个对象分配到堆上。其基本行为就是分析对象动态作用域:当一个对象在方法中被定义后,可以被外部方法调用,则称为方法逃逸

  对于定义在方法内部的对象,不会逃逸到其方法外部,经过JIT的逃逸分析,就会对其内存分配进行优化。结果就是,如果循环定义1万个对象,大多数会分配到栈上,减轻堆的压力。

猜你喜欢

转载自www.cnblogs.com/lemon-pomelo/p/9263664.html