理解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 需要经过解释执行,所以它的速度必然比可执行的二进制字节码程序慢。
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优化的细节。
使用寄存器优化
编译器通过决定何时从主存取值,何时向寄存器充值,来优化程序执行,减少开销。
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值在寄存器里循环,循环结束后,将最终的结果从寄存器返回给主存。这样就省去了从主存中检索值得开销。
使用方法内联优化
方法内联就是把方法的代码“复制”到发起调用的方法里,以消除方法调用。
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 编译器
- 浅谈对JIT编译器的理解。
- JIT编译器
- JIT编译器
- Java性能优化指南系列(三):理解JIT编译器
- Java性能优化指南系列(三):理解JIT编译器
- j9vm JIT编译器
- 深入浅出 JIT 编译器
- 深入浅出 JIT 编译器
- 深入理解java虚拟机(十三) Java 即时编译器JIT机制以及编译优化
- 深入理解java虚拟机(十三) Java 即时编译器JIT机制以及编译优化
- RyuJIT:下一代的JIT编译器
- JIT编译器
- [Java Performance] JIT编译器简介
- 解释器与JIT编译器
- 无法加载JIT编译器问题解决
- JVM的即时编译器JIT
- 探讨JVM的JIT 编译器
- 网易2017内推笔试题-数字游戏
- 父类引用指向子类对象
- Redis应用场景
- inline的使用
- CodeForces 501C Misha and Forest
- 理解JIT 编译器
- Java四类八种基本数据类型
- android color.xml
- Redis和其它键值对存储数据库有什么不同
- 使用RSA证书加密敏感数据
- ubuntu16.04开机循环输入密码无法进入桌面的解决办法
- 周末小记-ZooKeeper集群安装部署
- Layout属性笔记
- httpcomponent4.0调用样例