JVM运行期优化
Java程序在运行的期间,可能会有某个方法或者代码块的运行特别频繁时,就会把这些代码认定为“热点代码”。为了提高热点代码的执行效率,在运行时JVM会将这些代码编译成与本地平台相关的机器码,并进行各种层次的优化,完成这个任务的编译器称为即时编译器(Just In Time Compiler,JIT编译器)。
即时编译器(JIT)
事实上,现在许多主流的商用虚拟机,都同时包含有解释器与编译器,解释器与编译器两者各有优势。
与解释器相比,编译器会将常运行到的代码编译成本地代码区实现,可以获取更高的执行效率。而当程序运行环境中内存资源限制较大时,可以使用解释执行节约内存,反之可以使用编译执行来提高效率。
解释器和编译器之间还可以通过逆优化退回到解释状态继续执行,因此,在整个虚拟机执行架构中,解释器与编译器经常配合工作。
编译对象与触发条件
编译对象
在运行过程中会被即使编译器编译的“热点代码”有两类:
1,被多次调用的方法;
2,被多次执行的循环体;
触发条件
判断一段代码是不是为热点代码,是不是需要触发即时编译,这样的行为称为热点探测,但进行热点探测也是不一定要知道方法具体被调用了多少次,目前主要的热点探测判定方法有两种:
1,基于采样的热点探测:虚拟机会周期性地检查各个线程的栈顶,如果发现某个或者某些方法经常出现在栈顶,那这个方法就是“热点发放”。
优点:实现简单,高效。容易获取方法调用方法。
缺点:很难精确的定位一个方法的热点,容易受到线程阻塞或别的外界因素影响而扰乱热点探测。
2,基于计数器的热点探测:虚拟机会为每个方法(甚至是代码块)建立计数器,统计方法的执行次数,如果执行次数超过一定的阈值则认为它是热点方法。
优点:统计结构相对来说更加精准和严谨。
缺点:实现复杂,需要为每个方法建立计数器并维护,而且不能直接获取到方法的调用关系。
编译过程
在默认设置下,无论是方法调用产生即使编译请求,还是OSR编译请求,虚拟机在代码编译器还未完成之前,都仍然将按照解释方式继续执行,而编译动作则在后台的编译线程中进行。
编译优化技术
1.公共子表达式消除:
如果一个表达式E已经计算过了,并且从先前的计算到现在的E中所有变量的值都没有发生变化,那E的这次出现就成公共子表达式,可以用原先的表达式进行消除。
2.数组边界检查消除
系统将自动进行上下界范围检查。
3.方法内联
把目标方法的代码“复制”到发起调用的方法中,避免发生真实的方法调用。
4.逃逸分析
分析对象的动态作用域,当一个对象在方法被定义后,它可能被外部方法所引用,例如作为调用参数传递给其他方法,称为方法逃逸。甚至还有可能被外部线程访问到,称为线程逃逸。
如果能证明一个对象不会逃逸到方法或线程之外,也就是别的方法或线程无法通过任何途径访问到这个对象,就可以为这个变量进行一些高效的优化:如:栈上分配、同步消除、标量替换等。