Open funnycoding opened 4 years ago
操作系统:macOS 10.14.6
JDK_Version:1.8.072
JITWatch_Version:jitwatch-1.3.0
学习并发时看到这么一句话,关于 volatile 关键字的:「编译器将在volatile字段的读写操作前后各插入一些内存屏障。」。
于是我想去看看编译器是插入的内存屏障。
结果发现字节码中并没有在被 volatile 修饰的变量周围插入特殊的字节码。
后来经查询需要看的被 JIT 即时编译器编译过的汇编代码,这里就涉及到了对「汇编的反编译」。
操作系统:macOS 10.14.6
JDK_Version:1.8.072
JITWatch_Version:jitwatch-1.3.0
1. 动机
学习并发时看到这么一句话,关于 volatile 关键字的:「编译器将在volatile字段的读写操作前后各插入一些内存屏障。」。
于是我想去看看编译器是插入的内存屏障。
结果发现字节码中并没有在被 volatile 修饰的变量周围插入特殊的字节码。
后来经查询需要看的被 JIT 即时编译器编译过的汇编代码,这里就涉及到了对「汇编的反编译」。
### 2.准备工作 1. **下载**反汇编器:**HotSpot Disassembler** 其本质是一个动态链接库, MacOS使用的下载地址在这里 : https://github.com/liuzhengyang/hsdis/blob/master/build/macosx-amd64/hsdis-amd64.dylib 2. 将下载好的 `hsdis-amd64.dylib` 放到 `jdk/jre/lib` 下 例如我的目录是: `/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib` 3. 使用 `java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -version` 检测反汇编器是否 OK 如果输出中你能看到下图中的 `PringtAssembly is enabled` 就代表 OK 了 ![](https://xuyanxin-blog-bucket.oss-cn-beijing.aliyuncs.com/blog/20200409223354.png) 4. 安装 JITWatch ,GithubRepoUrl:https://github.com/AdoptOpenJDK/jitwatch 5. 编译/启动 JITWatch `gradlew clean build run` 因为 这个版本的 JITWATCH 是用 Gradle 来管理依赖,而我本人还是比较喜欢 Maven,因为每次使用Gradle 只要版本不同,就得下载一大堆 Gradle 的依赖,这次也不例外 如下图: ![image-20200409223827600](https://xuyanxin-blog-bucket.oss-cn-beijing.aliyuncs.com/blog/20200409223829.png) 下载好之后这个`shadowJar`就自动打开了,软件就长这样: ![](https://xuyanxin-blog-bucket.oss-cn-beijing.aliyuncs.com/blog/20200409223859.png) OK,现在反汇编编译器也有了,查看 JIT 编译后的代码也有了,下一步就是生成一个 JIT Log 文件,同时注意,JIT 优化热点代码的前提之一就是有一定的循环数量,我的代码是这样的: 是极客时间课程中的一个样例代码,我加了`100000`次循环 ```java public class VolatileExample { volatile int x = 0; volatile boolean v = false; public void writer() { x += 1; v = true; } public void reader() { if (v == true) { System.out.println(x); } } public static void main(String[] args) { VolatileExample example = new VolatileExample(); for (int i = 0; i < 100000; i++) { example.writer(); } } } ``` 6. 编译 VolatileExample.java 命令: javac VolatileExample.java 7. 配置执行时 JVM 参数,打印汇编日志文件 ```shell java -server -XX:+UnlockDiagnosticVMOptions -XX:+TraceClassLoading -XX:+PrintAssembly -XX:+LogCompilation -XX:LogFile=VolatileExample.log[即将生成的文件名] VolatileExample[你的类名] ``` 执行完毕后就得到了这么一个JIT Log文件 ![](https://xuyanxin-blog-bucket.oss-cn-beijing.aliyuncs.com/blog/20200409224345.png) 8. 使用 「JIT Watch」 打开生成好的 JIT 日志文件 1. 点击 OpenLog 打开刚才生成的JIT 文件 2. 点击 Config 配置 源码 和 Class 文件的地址,由于我的 源码 和 字节码文件都在一起,所以我配置的都是一个地址 3. 点击 `Start` 正常的话就可以看到左边 Packages 里面多出来了 你自己定义的类 4. 点击你自己定义的类,跳入 `TriView` 视图 ![](https://xuyanxin-blog-bucket.oss-cn-beijing.aliyuncs.com/blog/20200409224542.png) ![](https://xuyanxin-blog-bucket.oss-cn-beijing.aliyuncs.com/blog/20200409224901.png) 9. 阅读 JIT Watcher 反编译出来的汇编代码 ![](https://xuyanxin-blog-bucket.oss-cn-beijing.aliyuncs.com/blog/20200409225132.png) 可以看到这里就是被 `volatile` 修饰的变量 底层汇编代码对这个关键字做的处理 从 Java 源码到字节码再到汇编代码,一路跟下来,确认了理论正确之后,长舒了一口气,眼见为实。 如果你也有类似的需求,希望这篇文章能帮助到你。