虚拟机晚期(运行期)优化

216 阅读5分钟

概述

  1. 当虚拟机发现某个方法或代码运行特别频繁时,就会把这些代码认定为"热点代码"。为了提高热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成本地平台相关的机器码,完成这个任务的编译器称为即时编辑器(JIT编译器)

HotSpot虚拟机内部的即时编译器

一、解释器与编译器

  1. 当程序需要迅速启动和执行的时候,解释器可以首先发挥作用,省去编译的时间,立即执行。

  2. 编译器的作用,把代码编译成本地代码,可以获得更高的执行效率。

  3. 解释器可以作为编译器激进优化时的一个"逃生门"。

  4. 在整个虚拟机执行架构中,解释器与编译器配合工作。

  5. HotSpot虚拟机中内置了两个编译器。Client Compiler 和Senner Compiler称为C1编译器和C2编译器。

  6. 解释器与编译器搭配使用的方式咋虚拟机中称为"混合模式"。 二、编译对象与触发条件

  7. 被即时编译的"热点代码"有两类:被多次调用的方法、被多次执行的循环体。

  8. 两类热点代码编译器都会以整个方法作为编译对象。

  9. 前类使用JIT编译,后类使用OSR编译器。

  10. 判断一段代码是不是热点代码,这样的行为称为热点探测。

  11. 主要的热点探测判断方法有两种:基于采样的热点探测、基于计数器的热点探测。

    i. 采用这种方法的虚拟机会周期性的检查各个现场的栈顶,如果发现某个方法经常出现在栈顶,那这个方法就是"热点方法"。

    ii. 采用这种方法的虚拟机会为每个方法建立计数器,统计方法执行次数,如果执行次数超过一定的阈值,认为他是热点方法。

  12. HotSpot虚拟机采用计数器的热点探测方法,因此为每个方法准备了两类计数器:方法调用计数器和回边计数器。

  13. 当超过一定时间限度,方法调用次数,仍然不足以让它提交给即时编辑器,编译这个方法的调用计数器,会被减少一半,这个过程称为方法调用计数器的热度衰减,这段时间称为方法的半衰周期。

  14. 回边计数器,他的作用是统计一个方法中,循环体的代码执行次数。 三、编译过程

  15. Server Compiler它是一个简单的快速的段式编译器。

  16. 编译的三个阶段:

    i. 第一个阶段,一个平台的独立前端将字节码构造成一种高级中间代码表达式HIR。HIR使用静态但分配的形势来代表代码值。

    ii. 第二个阶段,一个平台相关的后端从HIR中产生低级中间代码表示(LIR)。

    iii. 最后一个阶段是在平台相关的后端,使用线性扫描算法,在LIR上分配寄存器,然后产生机器代码。

编译优化技术

一、公共子表达式消除

  1. 如果一个表达式E已经计算过了,并且从先前的计算到现在E中所有变量的值都没有发生变化,那么没有必要花时间在对E进行计算,直接使用,称为公共子表达式消除。 二、数组边界检查消除

  2. 如果编译器只要通过数据流分析就可以判断循环变量的取值范围,那么整个循环中就可以把数组上下界检查消除。 三、方法内联

  3. 方法内联看似简单,不过是把目标方法的代码"复制"到发起调用方法中。

  4. 编译器在进行内联时,如果非虚方法,那么直接进行内联。

  5. 如果遇到虚方法,则会查询此方法在当前程序是否有多个版本,可供选择,如果只有一个版本,那么可以进行内联,属于激进优化,需要预留一个"逃生门"称为保守内联。

  6. 如果查询出来的结果有多个版本目标方法可供选择,编译器会使用内联缓存来完成方法内联,这是一个简历在目标方法正常入口之前的缓存。大致原理是:在未发生方法调用之前,内联缓存状态为空,当第一次调用发生后,缓存记录方法接受者的版本信息,如果以后进来的每次调用的方法版本不一致,取消内联。 四、逃逸分析

  7. 当对象作为调用参数传到其他方法中,称为方法逃逸。

  8. 对象可能被外部线程访问到,称为线程逃逸。

  9. 对象如果不会逃逸到方法到线程上去可以进行的优化。

    i.栈上分配:如果确定一个对象不会逃逸出方法之处,那把这个对象在栈上分配内存。会随栈帧出栈而销毁。

    ii.同步消除:线程同步本身是一个比较耗时的过程,如果逃逸分析确定一个变量,不会逃逸出线程,那么这么变量实施的同步可以消除掉。

    iii.标量替换:标量是指一个数据无法分解成更小的数据表示了,如果一个额数据可以继续分解,称为聚合量。如果把一个对象拆散,将替换成标量,称为标量替换。

我是菜鸟,希望大家多多留言讨论~谢谢!

我的笔记是看完深入理解java虚拟机的体会,是本好书,推荐给大家.