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