JVM编译过程与后期的优化

文章目录

  • 一、JVM编译过程
  • 二、即时编译器优化(后期优化)
    • 1、编译器
    • 2、编译触发的条件
    • 3、编译优化技术(挑重要的讲几个)
  • 三、后记

一、JVM编译过程

引用自博客:https://blog.csdn.net/fuzhongmin05/article/details/54880257

1、词法分析:读取源码,找出词法中关键词那些不合法(分辨出这句话哪些是动词、哪些是标点)
2、语法分析:检查这些关键词组合在一起是不是符合Java语言规范。如if的后面是不是紧跟着一个布尔型判断表达式。(按照人的说话就是你用这些词组合在一起说话是不是正确
3、语义分析:将复杂的语法转化为简单的语法,对应到Java就是将foreach转化为for循环,还有一些注释等,最后生成一个抽象的语法树。(文言文翻译为白话文
4、字节码生成:将会根据经过注释的抽象语法树生成字节码,也就是将一个数据结构转化为另外一个数据结构。
语法分析就是检查源码是否符合java语法规范并将词组成语句。语义分析就是简化复杂的添加缺少的,检查变量类型是否合法。代码生成器就是遍历这棵树生成符合JVM规范的代码。

二、即时编译器优化(后期优化)

编译器与解释器 区分
(1)解释器通俗来讲就是一句一句执行,到哪算哪(不会加载到本地代码),下次运行代码又得解释执行;但是这样有利于迅速启动;
(2)编译器通俗来讲属于比较有规划,做事之前已经考虑好了(编译成本地),属于工欲善其事必先利其器,获得了更高的执行效率,下一次运行的时候直接使用本地代码。
(3)编译器由于激进优化(想得太多,各种找捷径,虽然大部分捷径是有用的),当激进优化假设不成立,此时解释器就是编译器的逃生门(智者千虑必有一失的时候(编译模式),采取无脑模式(解释器)作为后备方案)

1、编译器

虚拟机中有Client模式,简称为C1;Server模式,简称为C2
即时编译器编译本地代码需要占用程序运行时间,为了编译出优化程度更高的代码,解释器可能需要替编译器手机性能监控信息,为了在运行效率与程序启动响应速度做平衡,提出分层编译;
1、0层:程序解释执行,解释器不开启性能监控,可触发第一层编译
2、1层:c1字节码编译为本地代码,进行简单可靠的优化
3、2层(或者2层以上):C2编译,字节码编译为本地代码,进行编译时间较长的优化,根据解释器收集的性能监控信息进行激进优化

2、编译触发的条件

  • 热点代码:
    (1)被多次调用的方法
    (2)被多次执行的循环体
  • 热点探测:判断一段代码是不是热点代码,需不需要触发即时编译,这样的行为称为热点探测
    (1)基于采样的热点探测:周期性检查各个线程的栈顶
    (2)基于计数器的热点探测:每个方法建立计数器,提供了方法调用计数器(一个方法调用的次数)与回边计数器(同济一个方法体中一个循环体代码执行的次数)
  • 编译过程
    (1)Java方法入口
    (2)是否存在已编译版本,yes,执行编译后本地代码
    (3)No,计数器(包括方法计数器与回边计数器)+1
    (4)两计数器值和超过阈值,编译:
           (a)方法计数器:正常编译,超过一定时间,代码依旧不能提交给即时编译器,方法的调用计数器减少一半,使得系统运行时间足够长,绝大部分方法会被编译为本地代码
           (b)回边计数器:是为了触发OSR(栈上替换:编译动作由循环体触发,但是编译器依旧会以整个方法作为编译对象)编译
    (5)未超过阈值,解释器执行

C1:简单快速的三段式编译,关注与局部优化(优化手段后边会讲
1.、独立平台前端将字节码构造成高级中间代码HIR(转换之前进行:方法内联、常量传播)
2、平台后端将高级中间代码产生低级中间代码(转换之前进行:空值检查消除、范围检查消除)
3、使用线性扫描算法分配寄存器,做窥孔优化(不太了解),然后产生机器代码;
C2:面向于服务端的典型应用,为服务端的性能配置特别调整过的编译器,是一个充分优化过的高级编译器

3、编译优化技术(挑重要的讲几个)

  • 公共子表达式消除:语言无关的经典优化技术,计算过的表达式没必要重复计算,只需要替换就行
  • 数组边界消除:语言相关的经典优化技术,Java中为了避免数组越界,可以通过隐含的条件判断来避免数组溢出,这就会成为一种负担。编译器通过数据流分析判断循环变量取值范围,整个循环就可以把数组上下边界消除,节省很多条件判断
  • 方法内联:最重要的优化技术,用方法体代替方法调用,优点:去除方法调用成本(栈帧建立)和为其他优化建立基础

方法内联:对于有些方法在编译器就可以解析,但是针对一些具有动态属性的方法(运行期才知道具体的调用方法),比如虚方法,引入了CHA(类型继承关系分析),当前程序有多个版本可以选择的时候,也可以内联,不过属于激进优化,需要预留一个逃生门,称作守护内联,继承关系伪变化,内联优化一直使用,一旦继承关系改变,抛弃已经编译的代码,回退到解释状态,或者重新编译

  • 逃逸分析:最前沿的优化技术之一

逃逸(通俗来讲就是你在搞副业):一个对象被定义后,作为其他方法的参数(方法逃逸),其他线程访问该实例变量(线程逃逸)

如果当前对象不会逃逸,就会为该对象进行一些高效优化:
1、栈上分配:堆分配对象占据内存,垃圾回收都需要耗费时间,而将这些对象分配到栈上,一般没有逃逸的对象大部分属于局部对象,栈上对象可以随栈帧出栈而销毁,减小垃圾回收系统的压力
2、同步消除:如果没有逃逸,说明没有线程共同使用同一对象,针对该对象的同步操作可以消除
3、标量替换:标量:无法在分割的量,包括原始数据类型以及引用类型,聚合量(比如对象):可以再分解,如果该对象不会对外部访问,并且可分解,程序执行的时候将可能不创建这个对象,直接创建这个方法使用到的成员变量。

三、后记

本篇文章主要是为了总结后期编译优化,是在作者阅读了《深入理解JAVA虚拟机》后总结

你可能感兴趣的:(jvm)