理解JIT 编译器

来源:互联网 发布:经合组织数据库 编辑:程序博客网 时间:2024/05/21 09:19

前言

本文尝试用浅显的语言, 解释JIT的概念和基本原理,让读者明白JIT的运行方式和作用。最后,附上关于JIT的代码样例,帮助大家更好理解JIT。本文使用JVM虚拟机为Hotspot ,一切分析都在Hotpot上。如有不对的地方,欢迎指正。

JIT简介

JIT 是just in time 的缩写,即时编译编译器。

当JIT编译启用时, JVM读入字节码文件解释后,将其发给JIT编译器。JIT编译器将字节码编译成本机机器代码。

java代码编译过程

源代码经过javac编译,转换成java字节码文件(.class文件)。之后,JVM解释器将字节码文件翻译成对应的机器指令,逐条读入,逐条解释翻译。最后,对应的OS执行机器指令。

java code 需要经过解释执行,所以它的速度必然比可执行的二进制字节码程序慢。

Created with Raphaël 2.1.0源码javac是否需要JIT编译?JIT编译器机器码解释器yesno

JIT编译

通常,JVM执行代码时, 它并不立即开始JIT编译。
主要有两点原因

  • 代码本身只会被执行一次,那么从效率上来讲,对它进行JIT编译就是在浪费精力。因为JIT编译相对于解释器翻译字节码开销要大的多。
  • 当代码执行的次数越多, JIT就会越了解代码结构。JIT对代码的优化会更好。

那么什么时候会对代码进行JIT编译?

一般来说,当代码被频繁调用时(通常场景,代码中的循环体),该代码块将被JIT编译器编译。

JIT的编译阈值

在JVM中有两个计数器:

  • 方法被调用的次数
  • 方法中循环被回弹执行的次数

JVM在执行java方法时,它会坚持这两个计数器总和来决定是否需要JIT编译。
*-XX:CompileThreshold=N 可以配置这个阈值。在server模式下,它的默认值是10000;在client模式下,默认值是1500。
PS:不同的模式使用的JIT编译器是不同的。在client模式下, JVM使用的是一个代号为C1的轻量级编译器,而在server模式下,使用的是代号C2相对重量级的编译器。与C1相比,C2编译的更彻底,所以服务起来后,性能更高 *
2

JIT的优化策略

JIT的优化策略有很多,这里主要介绍两种比较普遍的优化,帮助大家理解JIT理解JIT优化的细节。

  1. 使用寄存器优化
    编译器通过决定何时从主存取值,何时向寄存器充值,来优化程序执行,减少开销。
    Java Code 的例子

      public class RegisterTest {        private int sum;        public void calculateSum(int n) {        for (int i = 0; i < n; ++i) {            sum += i;        }    }}

    在上面的例子中,sum的值在某些时刻可能在主存中,但是从主存中检索值是开销很大的操作。循环中可能多次从主存取值,性能很低。JIT编译器寄存器优化策略,会加载一个寄存器给sum并赋予其初始值,sum值在寄存器里循环,循环结束后,将最终的结果从寄存器返回给主存。这样就省去了从主存中检索值得开销。

  2. 使用方法内联优化
    方法内联就是把方法的代码“复制”到发起调用的方法里,以消除方法调用。
    Java Code 的例子

    public void caller(){        int a=1;        int b=2;        //do sth        int result =sum(a,b);    }    public int sum(int x,int y){        return x+y;    }

经过JIT编译器优化后,可能是转换为

    public void caller(){        int a=1;        int b=2;        //do sth        int result =a+b;    }

PS:以上例子只是帮助理解,并不能准确的反映JIT的方法内联优化。

JIT编译器优化实践

下面是一个简单的JIT优化的例子

package test.jit;/** * -XX:CompileThreshold=100000 * @author marshall * */public class JITTest {    public static int sum(int x, int y) {        int a = x + 1;        int b = y + 1;        int rs = a + b;        return rs;    }    public static int total(int n) {        int res = 0;        for (int i = 0; i < n; i++) {            res += sum(i, i);        }        return res;    }    public static void main(String[] args) {        int rs;        long bf;        long af;        long beforeJIT;        long afterJIT;        bf = System.nanoTime();        rs = total(100000);        af = System.nanoTime();        beforeJIT = af - bf;        System.out.println("程序没有经过预热即没有经过JIT优化,花费时间:" + beforeJIT + " 纳秒");        bf = System.nanoTime();        rs = total(100000);        af = System.nanoTime();        afterJIT = af - bf;        System.out.println("程序经过预热即经过JIT优化,花费时间:" + afterJIT + " 纳秒");        System.out.println("减少开销:" + (beforeJIT - afterJIT) / (beforeJIT * 1.0));    }}

手动设置程序阈值为100000,经过多次测试,运行测试越多, JIT优化性能提升越大。
jit性能优化

0 0
原创粉丝点击