优化作用
将热点代码编译成与本地平台相关的机器码,提高执行效率。这部分工作由即使编译器完成,即Just In-Time Compiller。
优化对象
- 被多次调用的方法
- 被多次执行的循环体
如何探测热点代码
- 基于采样。定期检查各个线程栈顶的方法,出现次数多的则很可能是热点代码。
- 基于计数器。用方法调用计数器、回边计数器分别来记录方法的调用次数以及循环体的执行次数。
我们聊一下后者——基于计数器的探测方法。其中方法计数器存在半衰期(-XX:-UseCounterDecay 开关),一定时间内计数器没有溢出(触发jit编译)则会减半。可能打开这个半衰期的特性可以让调用频率非常高的代码才进行jit编译,而关闭该特性后,系统运行足够长时间后绝大部分代码都会被即时编译为本地代码。回边计数器没有半衰期,我认为可能是由于循环体的JIT编译价值更高些才没有给回边计数器设计半衰期。
常见的优化手段
- 方法内联。减少方法调用的开销。关于方法内联,非虚方法可以被内联,虚方法也可以在CHA(Class Hierarchy Analisys)技术的支持下进行内联。如果方法只有一个版本(没被继承),则进行内联。如果存在多个版本(方法被继承),则使用内联缓存(inline cache),这是一种尝试性的内联。如果执行过程中,方法接收者(调用的对象)不变,则会使用内联,如果接受者变化了,才会使用虚方法表进行方法分派(动态分派)
- 公共子表达式消除。消除重复的计算。
- 数组边界检查消除。再保证不溢出的情况下,消除检查数组越界的代码。
- 逃逸分析。检查对象是否会被方法作用域外的方法、线程调用。基于该分析方法,可以进行 栈上分配(在栈上创建对象,提高垃圾回收效率)、同步消除(消除不必要的竞争锁的代码)、标量替换(避免创建对象,而使用基础类型)