java运行期优化

1.虚拟机发现某个方法或代码块运行的特别频繁,会把它认为是“热点代码”,为了提升执行效率,把热点代码编译成平台相关的机器码
JIT编译器

2.即时编译器:
1.解释器和编译器
需要迅速启动和执行的时候,解释器先发挥作用。时间久了,为了执行效率,编译器发挥作用

HotSpot虚拟机2个即时编译器 Client Compiler和Server Compiler(C1编译器,C2编译器)

虚拟机默认采用解释器与其中一个编译器配合。(取决于虚拟机的运行模式)  混合模式
可用参数-client -server强制指定虚拟机运行在Client模式还是Server模式

可用-Xint强制用解释模式。
-Xcomp强制用编译模式:有限用编译模式,在编译无法进行时还要用解释器

为了在启动响应速度和运行效率间平衡,采用分层编译:(Client Compiler:获取编译速度 和Server Compiler 获取编译质量 同时工作)
第0层:解释执行,解释器不开启性能监控
第1层:C1编译,字节码便以为本地代码,进行简单可靠的优化
第2层:C2编译,进行一些耗时较长的优化,甚至不可靠的激进优化

2.编译对象与触发条件:编译发生在方法执行过程中,也叫栈上替换
多次调用的方法
多次执行的循环体

判断是不是热点代码:
热点探测:
基于采样的热点探测:若发现某方法经常出现在栈顶,就是热点方法
基于计数器的热点探测:为每个方法建立计数器,统计执行次数。超过阈值就是热点代码,更精确严谨

基于计数器的热点探测:
方法调用计数器:Client模式1500次,Server模式10000次 可通过-XX:CompileThreshold人工设定,编译完成后,方法的调用入口地址就被系统自动改写成新的地址
不是方法调用的绝对次数,是一个相对的执行频率。一段时间内方法被调用的次数
超过一定时间限度(半衰周期),调用次数任然不足 计数器就减半:热度衰减 -XX:-UseCounterDecay关闭热度衰减
-XX:CounterHalfLifeTime:设置半衰周期

回边计数器(字节码中遇到控制流向后跳转的指令就叫回边):统计一个方法中循环体代码执行的次数
-XX:OnStackReplacePercentage间接设置回边计数器阈值 没有热度衰减

3.编译过程:后台编译
对于Client Compiler来说是一个简单快速的三段式编译器
第一阶段:一个平台独立的前端将字节码构成一种高级中间代码
第二阶段:一个平台相关的后端从HIR中产生低级中间代码
第三阶段:平台相关的后端使用线性扫描算法

Server Compiler:代码质量有所提高,减少本地代码执行时间。抵消编译时间开销

4.查看分析即时编译结果:
-XX:+PrintCompilation 要求虚拟机在即时编译时将被编译的本地代码方法名打出来
-XX:+PrintInlining 要求虚拟机输出方法内联信息



3.编译器优化:
方法内联:去除方法的调用成本(建立栈帧等),为其他优化建立良好基础(便于在更大范围进行后续优化)
冗余访问消除:y=a.value z=b.value --->z=y
复写传播
无用代码消除

公共子表达式消除:
表达式E倍计算过,且E中变量值未变,就没必要再计算E,直接用前面计算过的表达式结果代替E

数组便捷检查消除:
只要在编译器根据数据流分析确定数组的长度,判断下标有没有越界

方法内联:
目标方法代码复制到发起调用的方法中,避免真实的方法调用
静态方法,私有方法,实例构造器,父类方法直接内联
若遇到虚方法:想CHA查询次方法在当前程序下是否有多个版本选择

逃逸分析:
分析对象动态作用域:若能证明一个对象不会逃逸到方法或线程之外,别的方法 线程无法通过任何途径访问这个对象
1.栈上分配:对象在栈上分配内存:占用的空间就随着栈帧出栈而销毁
2.同步消除:不会逃逸到其他方法,线程,就不会有竞争。
3.标量替换:原始数据类型,不能再进一步分解。
对象称为聚合量:将访问对象的成员变量恢复为原始类型来访问就叫标量替换

判断一个对象是否逃逸,是一个相对耗时的过程,即时Server Compiler,也默认不开启
-XX:+DoEscapeAnalysis手动开启