Depth understanding of java virtual machine (19): compiler optimization techniques

 

 

 

 See the following code

org.xiaofeiyang.classloader Package; 

/ **
* @author: Yangchun
* @description:
* @date: the Created in 2019-12-02 17:03
* /
public class B {
static class A {
int value;
Final int GET ( ) {
return value;
}
}
public void foo () {
A = A new new A ();
int a.get Y = ();
int a.get Z = ();
int SUM = Y + Z;
}
}
Code optimization It must be based on the intermediate code or machine code level, inline method first step, two purposes. The first aim is to remove the method call cost, the second is to build a good foundation for other optimizations. The second step is the elimination of redundant access, y, z values have not changed, you can not access a local variable
common subexpression optimization will be int z = y. Replication for propagation third step, it is not necessary to use a tag variable z, directly on the line y = y. The fourth part is the elimination of useless code can be, y = y does not make sense, so the sum = y + y on it.
Four steps above the main use of the following techniques
1) common subexpression elimination
公共子表达式消除是一个普遍应用于各种编译器的经典优化技术,它的含义是:如果是一个表达式E已经被计算过,并且从先前的计算到现在E中所有的变量值都没有发生变化,E就成为了公共子表达式。对于这种表达式,没有必要花时间再对它进行
计算,如果这种优化仅限于程序的基本块内,便称为局部公共子表达式消除,如果这种优化范围涵盖了多个基本模块,那就称为全局公共子表达式消除。
int d=(b*c)*12+a+(a+b*c),未优化字节码如下

 

 

 

 这段代码进入虚拟机即时编译器后,编译器检查到b*c,c*b是一样的表达式而且计算期间b与c的值只是不变的,因此,这条表达式就可被视为:

int d=E*12+a+(a+E)


2)数组范围检查消除
java每次数组的读写,对于大量数组操作访问的程序代码,性能开销很严重。这种运行期检查提到编译期完成的思路外,另外还有一种避免思路-隐式的异常处理如下代码
if(foo!=null){
  return foo.value
}else{
  throw new NullPointException()
}
虚拟机处理上面代码会注册一个SegmentFault信号的异常处理,这种情况不会增加异常foo判断空的额外开销,代价是如果真为空,必须从用户态转入内核态结束后再转换成用户态。当foo极少为空的情况下可以这么操作,虚拟机足够聪明
它会根据运行期收集到的Profile信息自动选择最优方案。
try{
  return foo.value
}catch(segment_fault){
  uncommon_trap()
}
3)方法内联
java方法解析和分派调用时候就介绍过。只有使用invokespecial指令调用的私有方法,实例构造器,父类方法,以及使用的invokestatic指令进行调用的静态方法才是在编译期进行解析。其余的方法java调用都需要在运行时方法接受者的
多态选择,简而言之java实例调用的方法默认是虚方法。为了解决虚方法内联问题,java虚拟机团队想出来了一个类型继承关系分析技术。这是一种基于整个应用程序的类型分析技术,它用于确定目前加载的类中,某个接口是否有多于一种的实现,
某个类是否存在子类,子类是否为抽象类的信息。如果遇到虚方法会向CHA查询此方法在当前版本有多少目标可以选择,如果只有一个直接进行内联,这个属于激进优化,要预留一个逃生门。如果没有加载指令可以导致继承关系发生变化的新类,
那就需要抛弃已经编译的代码,退回到解释执行的状态或者重新编译。如果有多个版本可以提供选择,使用内联缓存这是建立在目标方法正常入口之前的缓存,第一次调用缓存为空,第一次调用缓存记录下方法接收者的版本信息,并且每次进行调用
时都比较接收者版本,如果以后进来的每次方法调用接收者一致就可以走缓存,那这个内联就可以一直用下去,如果发生了方法接收者不一致的情况,就说明程序真的使用虚方法的特性,查找虚方法表进行分派,并且取消内联。
4)逃逸分析
分析对象动态作用域,当一个对象被定义后,它可能被外部方法引用。作为参数传递给外部,被称为方法逃逸,被外部线程访问到,赋值给类变量或其他线程中可以访问的变量实例被称为线程逃逸。如果一个对象不能逃逸,则可能为这个变量进行一些高效的优化
虚拟机回收机制可以回收堆对象,不可以回收栈对象,整理和回收内存都需要时间。如果确定一个对象不会逃逸,那让这个对象在栈上分配内存,对象所占空间也会随着栈帧出栈而销毁。同步消除,不能逃逸对象不需要进行同步。标量替换标量是指
一个数据已经无法分解成更小的数据表示,java虚拟机的原始数据类型。如果一个对象拆散,根据程序访问的情况,将其使用的成员变量都变成原始类型来访问,叫做标量替换。如果逃逸分析一个对象不会被外部访问到,并且可以拆分,程序真正执行的
时候将可能不会创建这个对象,改为直接创建它的若干被这个方法使用到的成员变量来替代。将对象拆分拆分后除了变量可以直接在栈上创建以外,有很大概率会被虚拟机分配到高速寄存器中存储和读写,还可以为下一步优化手段创建条件。目前
虚拟机只能使用时间压力相对比较小的来完成逃逸分析。如果有需要-XX:+DoEscapeAnalysis开启逃逸分析,参数-XX:+PrintEscapeAnalysis查看分析结果。使用-XX:+EliminateAllocations开启标量替换,-XX:+EliminateLocks开启
同步消除,-XX:+PrintEliminateAllocations查看标量替换情况。


Guess you like

Origin www.cnblogs.com/xiaofeiyang/p/11973256.html