前言
提示:最近在写volatile的时候,涉及到了编译器优化的部分内容,写在voaltile里内容有点臃肿,单独写一篇博客来聊聊好了,直接先上一段代码:
public class VolatileClass { private static boolean isRunning = true; private static int i = 0; public static void main(String[] args) { Thread thread = new Thread(() -> { while (isRunning) { i++; } }); thread.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } isRunning = false; System.out.println("isRunning has been changed to false"); } }
一、JIT 即时编译器的分类
我们知道,HotSpot集成了两个JIT compiler — C1及C2(或称为Client及Server)
两者的区别在于,前者没有应用激进的优化技术,因为这些优化往往伴随着耗时较长的代码分析。因此,C1的编译速度较快,而C2所编译的方法运行速度较快。
这两个JIT compiler(C1与C2)以及interpreter(解释器)将HotSpot的执行方式划分为五个级别:
level 0:interpreter解释执行
level 1:C1编译,无profiling(性能监控功能)
level 2:C1编译,仅方法及循环back-edge执行次数的profiling((性能监控功能))
level 3:C1编译,除level2中的profiling外还包括branch(针对分支跳转字节码)及receivertype(针对成员方法调用或类检测,如checkcast,instnaceof,aastore字节码)的profiling
level 4:C2编译
二、优化结果查看
1. 无volatile C2-level4级
可以看到jmp L0000 回跳到了inc 指令,也就是我们代码里的i++。换句话说,第一次进入循环会读我们的isRuning变量,读入寄存器后,做一个判断,但是也仅有一次判断。后续直接把循环判断的判断给去掉了,直接变成无限执行循环体了
2. 有volatile C2-level4级
我们对isRuning加了volatile,然后再运行看看情况
可以看到对isRuning加了volatile之后,编译器编译后的结果发生了改变,每次进行循环之前,会对isRuning再做一遍取值,如果不为0,则跳转到L0000,即满足条件再执行循环体的内容