JVM编译优化

参考文章:
  • 方法内联: https://www.toutiao.com/a6530421233337500164/?tt_from=weixin&utm_campaign=client_share×tamp=1520505006&app=news_article&utm_source=weixin&iid=27056549301&utm_medium=toutiao_ios&wxshare_count=1
  • 整体描述: https://blog.csdn.net/qq_16681169/article/details/72945113
  • 标量替换: https://blog.csdn.net/qiang_zi_/article/details/100147613
  •  
虚拟机会用即时编译器把运行频繁的热点代码变成机器码,并做相应优化。
 
1 解释器和编译器的优点
解释器优点:
程序启动快,占用内存小,编译失败时还能逆优化恢复到解析状态
 
编译器优点:
运行时可探测热点代码,并把字节码编译成本地机器码,这样程序执行效率更高
 
2 在什么情况下才会被判定为热点代码?需要编译成机器码了?
JVM启动的时候只有解释器进行解析,启动完之后会根据运行状态,然后依据运行模式(client、server模式,默认服务端默认都用server模式)来进行运行时编译。
 
如何判断为热点代码(多次调用的方法/循环体)
- 基于采样热点探测:
经常出现在栈顶,可判断为热点代码,但对于循环体不好判断
 
- 基于计数器热点探测(Hotspot就是用这种)
对调用次数加一个计数器,调用方法(用 方法调用计数器)/循环体(应用 回边计数器)此次到达上限,就会被编译成机器码。
备注:上面在没编译完成之前,都是用解析器执行字节码来运行程序,并且有些代码编译不成功就会退回用解释器解析。
 
 
 
3 下面说下不同模式下编译器做的优化
3.1 Client模式(C1编译器)优化
只针对耗时较高的做优化,占内存小,主要有方法内联、去虚拟化、冗余消除等,做的局部优化
 
1 方法内联
多方法调用,参数传来传去,还要压栈弹栈,这收他就把被调用的方法复制出来,避免频繁弹栈出栈,例子如下:
=========================
int a(int val) {
    return val + b(val);
}
int b(int val) {
    return val * val;
}
优化后如下:
int a(int val) {
    return val + val * val;
}
=========================
其阈值设置参数如下:
-XX:CompileThreshold
-- client,默认为1500
-- server,默认为10000
 
优化参数建议:
针对热点方法,想要通过JIT内联优化来提升性能的建议
1. 更小的方法体,JVM总是偏好更小的方法。
2. 尽量使用final、private、static修饰符,避免因为继承而需要额外的类型检查
3. 使用+PrintInlining参数校验效果
 
 
2 去虚拟化
装在.class的时候,发现类只有一个实现类,那调用该类实现类的方法时,就直接进行内联(对,就是1 的内联 (╬▔皿▔)凸)来优化
 
3 冗余消除
编译时把多余无用代码折叠或消除
 
3.2 Server模式(C2编译器)优化
占用内存多,大量优化,适合服务端,其优化有无用代码消除、循环展开、循环表达式外提、消除公共子表达式、常量传播、基本块冲排序等,并且会做C1编译器相关的优化
 
1 逃逸分析
C2优化的基础,根据运行状态来判断方法中的变量是否会被外部读取,如不会则认为此变量是不会逃逸的,那么在编译时会做标量替换、栈上分配和同步消除等优化。
- 方法逃逸:当前对象被外部方法引用
- 线程逃逸:当前对象被外部线程访问
 
1.1 标量替换
替换为标量(基本的数据类型如int、long),通过标量替换将该对象分解,并在栈上分配内存
 
1.2 栈上分配
直接在栈上创建实例,而不用去堆上创建,这样分配速度快,回收方便
 
1.3 同步消除
如果发现对象不存在竞争,就会把他的锁标识消除,这样读写就不会有竞争
 
4 编译期优化(早期优化)
优化语法糖,比如(不止三种):
1 泛型:编译完成后替换为原生类型
 
2 条件编译:比如发现必传什么值,就会把他预先处理好,如下:
if (false) {
    //xxxx
}
-- 比如上面发现if里面必为false,就会直接消除掉这个代码
 
3 自动装箱/拆箱:会转为基本数据类型
 
5 运行时优化(晚期优化)
Hotspot采用解析器和编译器并行策略,具体优化如下:
 
1 公共字表达式消除
发现这个表达式上面已经计算过了,下面直接用上面的结果值就行了,无需再次计算
 
2 数组边界检查消除
发现代码肯定不会越界,那直接不检查边界
 
3 方法内联
 
4 逃逸分析
 
6 与C++比较
性能上比不过C++(因为他全是机器码),但是可以做监控、热点探测并且有选择性地优化代码
 
 

你可能感兴趣的:(JAVA)