HotSpot的即时编译器
解释器与编译器
编译对象与触发条件
编译过程
编译优化技术
如果还对其他的经典编译优化技术感兴趣,可以参考《编译原理》
(俗称“龙书”,推荐使用Java的程序员
阅读2006年版
的“紫龙书”)中的相关章节。
优化技术概览
语言无关的经典优化技术之一:公共子表达式消除。
语言相关的经典优化技术之一:数组范围检查消除。
最重要的优化技术之一:方法内联。
最前沿的优化技术之一:逃逸分析。
公共子表达式消除
含义:如果一个表达式E已经计算过了,并且从先前的计算到现在E中所有变量的值都没有发生变化,那么E的这次出现就成为了公共子表达式。
对于这种表达式,没有必要花时间再对它进行计算,只需要直接用
前面计算过的表达式结果代替
E就可以了。
如果这种优化仅限于程序的基本块内
,便称为局部
公共子表达式消除(LocalCommonSubexpressionElimination),
如果这种优化的范围涵盖了多个基本块
,那就称为全局
公共子表达式消除(GlobalCommonSubexpressionElimination)。
数组边界检查消除
如果数组下标是一个常量,如foo[3],只要在编译期根据数据流分析来确定foo.length的值,并判断下标“3”没有越界,执行的时候就无须判断了。
更加常见的情况是数组访问发生在循环之中
,并且使用循环变量来进行数组访问,如果编译器只要通过数据流分析就可以判定循环变量的取值范围永远在区间[0,foo.length)之内,那在整个循环中就可以把数组的上下界检查消除,这可以节省很多次的条件判断操作
。
隐式异常处理
,Java中空指针检查
和算术运算中除数为零的检查
都采用了这种思路。
if(foo!=null){
returnfoo.value;
}else{
thrownewNullPointException();
}
//在使用隐式异常优化之后,虚拟机会把上面伪代码所表示的访问过程变为如下伪代码。
try{
returnfoo.value;
}catch(segment_fault){
uncommon_trap();
}
虚拟机会注册一个SegmentFault信号的异常处理器(伪代码中的uncommon_trap()),这样当foo不为空的时候,对value的访问是不会额外消耗一次对foo判空的开销的。代价就是当foo真的为空时,必须转入到异常处理器中恢复并抛出NullPointException异常,这个过程必须从用户态转到内核态中处理,结束后再回到用户态,速度远比一次判空检查慢。
当foo极少为空的时候,隐式异常优化是值得的,但假如foo经常为空的话,这样的优化反而会让程序更慢。
还好HotSpot虚拟机足够“聪明”,它会根据运行期收集到的Profile信息自动选择最优方案。
方法内联
方法内联(MethodInlining),方法内联的重要性
要高于其他优化措施,它的主要目的
有两个
:
一是去除方法调用的成本(如建立栈帧等)。
二是为其他优化建立良好的基础,方法内联膨胀之后可以便于在更大范围上采取后续的优化手段,从而获取更好的优化效果。因此,各种编译器一般都会把内联优化放在优化序列的最靠前位置
。
逃逸分析
不是直接优化代码的手段,而是为其他优化手段提供依据的分析技术。
逃逸分析的基本行为
就是分析对象动态作用域
:当一个对象在方法中被定义后,它可能:
- 被外部方法所引用,例如作为
调用参数
传递到其他方法中,称为方法逃逸
。 - 被外部线程访问到,譬如
赋值给类变量
或可以在其他线程中访问的实例变量
,称为线程逃逸
。
网友评论