Java Compile and Optimize

1. 早期(编译期)优化

1-1. 概论

  1. 前端编译器:Sun的Javac、 Eclipse JDT中的增量式编译器(ECJ)
    • JIT编译器:HotSpot VM的C1、 C2编译器
    • AOT编译器:GNU Compiler for the Java(GCJ)、 Excelsior JET
  2. javac这类编译器几乎不对代码进行性能优化,把性能优化放到了JIT即时编译器内,可为JRuby、Groovy这类语言的代码也同等享有优化带来的好处

1-2. javac编译

1-2.1. 编译过程

  1. 解析与填充符号表过程
  • 词法、语法分析
    • 词法分析是将源代码的字符流转变为标记(Token)集合,单个字符是程序编写过程的最小元素,而标记则是编译过程的最小元素,关键字、 变量名、 字面量、 运算符都可以成为标记
    • 语法分析是根据Token序列构造抽象语法树的过程,抽象语法树(Abstract Syntax Tree,AST)是一种用来描述程序代码语法结构的树形表示方式,语法树的每一个节点都代表着程序
      代码中的一个语法结构(Construct),例如包、 类型、 修饰符、 运算符、 接口、 返回值甚至代码注释等都可以是一个语法结构.
  • 填充符号表
    • 符号表(Symbol Table)是由一组符号地址和符号信息构成的表格
    • 在语义分析中,符号表所登记的内容将用于语义检查(如检查一个名字的使用和原先的说明是否一致)和产生中间代码.在目标代码生成阶段,当对符号名进行地址分配时,符号表是地址分配的依据.
  1. 插入式注解处理器的注解处理过程
  2. 分析与字节码生成过程
  • 标注检查
    • 检查的内容包括诸如变量使用前是否已被声明、 变量与赋值之间的数据类型是否能够匹配等
    • 常量折叠 如定义了int a = 1 + 2会被编译为int a = 3
  • 数据及控制流分析
    • 数据及控制流分析是对程序上下文逻辑更进一步的验证,它可以检查出诸如程序局部变量在使用前是否有赋值、
      方法的每条路径是否都有返回值、 是否所有的受查异常都被正确处理了等问题.
  • 语法糖
    • 语法糖:也称糖衣语法,是由英国计算机科学家彼得·约翰·兰达(Peter J.Landin)发明的一个术语,
      指在计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用.
      通常来说,使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会.
    • java语法糖:泛型、变长参数、 自动装箱/拆箱等 内部类、 枚举类、 断言语句、 对枚举和字符串(在JDK 1.7中支持)
      的switch支持、 try语句中定义和关闭资源(在JDK 1.7中支持)等
    • 泛型:编译器会进行解泛,所以泛型不能进行方法重载
    • 自动装箱、 拆箱与遍历循环
    • 条件编译 if 的条件为常量时编译期会被执行
  • 字节码生成
    • 字节码生成阶段不仅仅是把前面各个步骤所生成的信息(语法树、 符号表)转化成字节码写到磁盘中,
      编译器还进行了少量的代码添加和转换工作.

2. 晚期(运行期)优化

2-1. 即时编译器(JIT)

  1. 为了提高热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化,
    完成这个任务的编译器称为即时编译器(Just In Time Compiler)
  2. 解释器与编译器(sun HotSpot虚拟机为例)
  • 交互模型
            +---------------即时编译-----------↓
            ↓                         Client Compiler(C1编译器)
         解释器(Interpreter)               编译器
            ↑                         Server Compiler(C2编译器)
            +----------------逆优化------------↑
    
  • HotSpot虚拟机同时由解释器与编译器搭配使用(成为混合模式(Mixed Model)),
    可添加参数-Xint强制虚拟机使用解释模式(Interpreted Mode),可添加参数-Xcomp强制虚拟机使用编译模式(Compiled Mode)
  • 启动参数-client启用Client Compiler编译器 参数-server启用Server Compiler编译器
  • HotSpot虚拟机还会逐渐启用分层编译(Tiered Compilation)的策略,分层编译的概念在JDK 1.6时期出现,
    JDK1.7中Server模式中默认开启,之前需配参数-XX:+TieredCompilation启用
  1. 编译对象与触发条件
  • 触发条件
    • 被多次调用的方法
    • 被多次执行的循环体,这个称为栈上替换(On Stack Replacement,简称为OSR编译)
  • 热点代码判定
    • 基于采样的热点探测,周期性检测个线程的栈顶,若某个方法经常在栈顶,则认为是热点方法;
      优点是实现简单简单,高效,容易获取方法调用关系,缺点是不能准确判定一个方法的热度
    • 基于计数器的热点探测,虚拟机为每个方法设定计数器,统计方法执行次数,若超过一定阈值则认为是热点方法;
      优点是能准确判定一个方法的热度,缺点是要为每个方法建立计数器并维护,实现麻烦,也不能获取方法的调用关系
  • HotSpot虚拟机是基于计数器的热点探测,有2个计数器:方法调用计数器(Invocation Counter)和回边计数器(Back Edge Counter)
    • 方法调用计数器,Client模式下阈值为1500次,Server模式下阈值为1000次,可由参数-XX:CompileThreshold来设置
      -XX:-UseCounterDecay设置关闭热度衰减,-XX:CounterHalfLifeTime参数设置半衰周期的时间,单位是秒
    • 回边计数器,参数-XX:OnStackReplacePercentage来间接调整回边计数器的阈值
      Client模式下阈值计算公式:
      方法调用计数器阈值(CompileThreshold)×OSR比率(OnStackReplacePercentage)[默认值为933]/100 默认情况下为13995
      Server模式下阈值计算公式:
      方法调用计数器阈值(CompileThreshold)×(OSR比率(OnStackReplacePercentage)[默认值140]-解释器监控比率(InterpreterProfilePercentage)[默认值33]/100
      默认情况下为10700
  1. 编译过程
    • Client模式下编译过程
      Client Compiler编译过程
    • Server模式下编译过程
      相当复杂
  2. 优化技术
  • 公共子表达式消除
    • 经典优化技术,如果一个表达式E已经计算过了,并且从先前的计算到现在E中所有变量的值都没有发生变化,那么E的这次出现就成为了公共子表达式.
      对于这种表达式,没有必要花时间再对它进行计算,只需要直接用前面计算过的表达式结果代替E就可以了
  • 数组边界检查消除
    • 语言相关的其他消除操作还有不少,如自动装箱消除(Autobox Elimination)、 安全点消除(Safepoint Elimination)、消除反射(Dereflection)等
  • 方法内联
    • 虚拟机最重要的优化手段之一,除了消除方法调用的成本之外,它更重要的意义是为其他优化手段建立良好的基础
    • 非虚方法直接内联,虚方法引入了一种名为“类型继承关系分析”(Class Hierarchy Analysis,CHA)的技术,
      检查发现没有多个目标版本可供选择,则也可内联,但需准备一个逃生门,即使有多个版本目标也会默认内联,但在调用时要检查,
      发现版本目标不一致在取消内联,会从“逃生门”回到解释状态重新执行
  • 逃逸分析(JDK1.6)
    • 当下java最前沿的优化技术
    • 逃逸分析的基本行为就是分析对象动态作用域:当一个对象在方法中被定义后,它可能被外部方法所引用,
      例如作为调用参数传递到其他方法中,称为方法逃逸. 甚至还有可能被外部线程访问到,
      譬如赋值给类变量或可以在其他线程中访问的实例变量,称为线程逃逸.
    • 栈上分配(Stack Allocation),如果证明一个对象不会逃逸到方法之外,则可以将对象分配到方法栈帧内存,这样随着栈帧出栈而销毁,极大地降低了GC系统压力
    • 同步消除(Synchronization Elimination),如果能证明一个变量不会逃逸出线程,那就可以消除掉同步措施,消除同步带来的消耗
    • 标量替换(Scalar Replacement)
      标量(Scalar),是指一个数据已经无法再分解成更小的数据来表示了,如:int,long,double
      聚合量(Aggregate),是指一个数据可以被分解,典型的java对象
      如果证明一个对象不被外界访问,又可拆散的话,那程序在调用的时候就不创建该变量,改为创建多个成员变量来代替,
      将对象拆分后,除了可以让对象的成员变量在栈上(栈上存储的数据,有很大的概率会被虚拟机分配至物理机器的高速寄存器中存储)
      分配和读写之外,还可以为后续进一步的优化手段创建条件
    • 逃逸分析尚不成熟

上一篇
Database Database
一. 四大特性(CIAD) 原子性(Atomicity): 要么全成功,要么全失败,失败会回滚。 一致性(Consistency): 一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须
2018-06-18
下一篇
Java GC Java GC
一. Java虚拟机内存区域1. 运行时数据区 2. 程序计数器(Program Counter Register) 1、程序计数器是线程内(每个线程都有唯一的、封闭的)一小块内存区域 2、计数器指定的是当前虚拟机执行指令的地址 3、
2018-06-15