jvm虚拟机编译期优化

java语言的“编译期”是一段“不确定”的操作过程,因为它可能是指一个前端编译器把*.java文件转变为*.class文件的过程(Javac、Eclipse JDT);也可能是指虚拟机的后端运行期编译器(JIT编译器,just in time compiler)把字节码转变为机器码的过程;还可能是指使用静态提前编译器(AOT编译器)直接把*.java编译成机器码的过程(GCJ)。
接下来记录的均是前端编译器相关的知识
注:前端编译器对运行效率没有优化,对性能的优化集中到了后端即时编译器中,但是做了很多优化措施来改善程序员的编码风格和提高编码 效率
编译过程(此过程是将*.java编译成*.class,即编译成字节码):
1.词法、语法分析
1)词法分析是将源代码的字符流转化为标记(Token)集合
2)语法分析
根据Token序列构造抽象语法树(所谓抽象语法树是一种用来描述程序代码语法结构的树形表示方式,每个节点都代表着程序代码中的一个语法结构)
生成抽象语法数之后编译器基本就不会对源码文件进行操作了,后续的操作都建立在抽象语法树之上

2.填充符号表
符号表由一组符号地址和符号信息构成的表格,符号表所登记的内容将用于编译的不同阶段

3.注解处理器
注解与普通java代码一样,是在运行期间发挥作用,如果注解修改了抽象语法树,编译器将回到解析及填充过程重新处理,直到所有注解都没有修改语法树为止

4.语义分析
对结构上正确的原程序进行上下文有关性质的检查
1)标注检查
检查诸如变量使用前是否已被申明、变量与赋值之间的数据类型是否匹配等
2)数据即控制流分析
3)解语法糖
语法糖能增加程序的可读性,但对程序功能没有影响
5.字节码生成
将前面各个步骤生成的信息转化为字节码写到磁盘中,还进行少量代码的添加和转化工作(如构造器等)

java语法糖
1.泛型与类型擦除
java语言中的泛型只在程序源码中存在,在编译后的字节码文件中就已经替换成原来的原生类型(Raw Type),并且在相应的地方插入强制转型代码,所谓的擦除是对方法中的code属性中字节码进行擦除,实际上元数据中还是保留泛型信息(这是我们能通过反射手段取得参数化类型的根本依据)。
2.自动装箱、拆箱与增强for循环

晚期(运行期)优化
在运行时,虚拟机会把“热点代码”编译成与本地平台相关的机器码,并进行各种层次的优化
1)主流商用虚拟机都同时包含解释器和编译器,当程序需要快速启动和执行时,解释器可以首先发挥作用,省去编译的时间,能立即执行,在程序运行后,编译器逐渐发挥作用,把越来越多的代码译成本地代码,获得更高的执行效率
2)在内存资源限制较大的环境可以使用解释执行节约内存,反之有编译执行提高效率
3)解释器可以作为编译器激进优化的”逃生门“,即在优化时出现问题,可以退回到解释状态继续执行
hotshpot虚拟机内置两个即时编译器,分别称为client complier和server complier或简称c1和c2编译器
1. 即时编译器的触发条件
"热点代码"主要分为两类:
1)被多次调用的方法
2)被多次执行的循环体(编译器依然以整个方法作为编译对象)
判断一段代码是不是热点代码
法一:基于采样的热点探测:虚拟机周期性检查各个线程的栈顶,如果发现某方法经常出现则为热点代码
法二:基于计数器的热点探测,虚拟机为每个方法建立计数器,统计方法的执行次数
hotspot使用第二种方法
注:虚拟机设计团队几乎把所有对代码的优化措施集中到了即时编译器中

常见的优化技术:
1.公共子表达式消除
如果一个表达式E已经计算过了,并且从先前的计算到现在E中所有变量的值都没有发生改变,那么E的这次出现就成了公共子表达式,不需再对它进行计算,只需用前面的结果代替即可
2.数组边界检查消除
如果编译器通过分析就可以判定循环变量永远不会越界,则可以把数组的上下界检查消除,节省了多次条件判断
3.方法内联
将目标方法的代码“复制”到发起调用的方法之中,避免发生真实的方法调用,为其它优化手段建立良好的基础
编译器在进行内联时,如果时非虚方法,那么就直接进行内联即可,如果遇到虚方法则通过引用一种名为”类型继承关系分析“技术来分析目前已加载的类中某个接口是否有多于一种的实现,某个类是否存在子类等信息,如果CHA查询此方法在当前程序下只有一个版本,那就可以内联,但需要预留一个”逃生门“,一旦加载导致继承关系发生变化的类,就退回解释状态执行,一次内联是一种激进优化
4 逃逸分析
是目前java虚拟机中比较前沿的优化技术,也是为其它优化手段提供依据的分析技术

1.栈上分配
2.同步消除
3.标量替换
即时编译器同c/c++的静态化编译器相比有如下缺点

你可能感兴趣的:(java基础知识)