前言
注意:javap是为了让我们看.class字节码文件,因为直接看,不太方便;而.class在jvm中,需要进一步转为话硬件能识别的汇编语言(当然,进cpu之前,需要进一步把汇编语言再转为二进制形式),本篇目的就是为了查看汇编语言,即反汇编。
看.class字节码请参考《javap的使用(对字节代码进行反编译工具)》
HSDIS
HSDIS(HotSpot disassembler),一个Sun官方推荐的HotSpot虚拟机JIT编译代码的反汇编插件,其实际上就是一个动态库。这里我们直接从网上下载与我们系统对应的编译后文件,然后直接将其放置到JDK的bin目录下即可
1.下载工具hsdis-amd64.dll反汇编程序插件
http://vorboss.dl.sourceforge.net/project/fcml/fcml-1.1.1/hsdis-1.1.1-win32-amd64.zip
http://vorboss.dl.sourceforge.net/project/fcml/fcml-1.1.1/hsdis-1.1.1-win32-i386.zip
注意:如果是32位的jdk,需要下载32位的dll
-
将hsdis-amd64.dll放在 $JAVA_HOME/jre/bin/server 目录下
注意目录是jre的目录
-
运行时添加参数: -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly
查看Volatile例子
-server -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:CompileCommand=compileonly,*VolatileTest.refresh
-XX:CompileCommand=compileonly,VolatileTest.refresh表示过滤,仅显示VolatileTest.refresh,注意号不能丢,除非你提供类的全路径名称
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class VolatileTest {
//循环的终止条件,加volatile会影响运行结果
private static volatile boolean initFlag = false;
private static int counter = 0;
public static void refresh(){
log.info("refresh data.......");
initFlag = true;
log.info("refresh data success.......");
}
public static void main(String[] args){
//线程A负责循环,直至收到终止的信令才会停下来
Thread threadA = new Thread(()->{
while (!initFlag){
//System.out.println("runing");
counter++;
}
log.info("线程:" + Thread.currentThread().getName()
+ "当前线程嗅探到initFlag的状态的改变");
},"threadA");
threadA.start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//b线程负责通知A终止循环
Thread threadB = new Thread(()->{
refresh();
},"threadB");
threadB.start();
}
}
关键输出信息:
0x0000000002bc7057: lock add dword ptr [rsp],0h ;*putstatic initFlag
; - com.yg.edu.jmm.VolatileTest::refresh@11 (line 18)
能看到initFlag变量前有lock标识
错误
如果不下载dlll,运行时会报错:
Could not load hsdis-amd64.dll; library not loadable; PrintAssembly is disabled
过程解释
-XX:CompileCommand作用:
参见《你假笨JVM参数 - 003 CompileCommand》
含义:
该参数用于定制编译需求,比如过滤某个方法不做JIT编译
若未指定方法描述符,则对全部同名方法执行命令操作,具体如何指定见下文[举例]
可使用星号通配符(*)指定类或方法,具体如何使用见下文[举例]
该参数可多次指定,或使用 换行符(\n)分隔参数后的多个命令
解析完该命令后,JIT编译器会读取.hotspot_compiler文件中的命令,该参数也可写在.hotspot_compiler文件中
可使用-XX:CompileCommandFile指定.hotspot_compiler文件为其他文件
用法:
-XX:CompileCommand=command,method[,option]
命令:
exclude,跳过编译指定的方法
compileonly,只编译指定的方法
inline/dontinline,设置是否内联指定方法
print,打印生成的汇编代码
break,JVM以debug模式运行时,在方法编译开始处设置断点
quiet,不打印在此命令之后、通过-XX:CompileCommand指定的编译选项
log,记录指定方法的编译日志,若未指定,则记录所有方法的编译日志
其他命令,option,help
举例:
1. 设置编译器跳过编译com.jvmpocket.Dummy类test方法的4种写法
-XX:CompileCommand=exclude,com/jvmpocket/Dummy.test
-XX:CompileCommand=exclude,com/jvmpocket/Dummy::test
-XX:CompileCommand=exclude,com.jvmpocket.Dummy::test
-XX:CompileCommand="exclude com/jvmpocket/Dummy test"
2. 设置编译器只跳过编译java.lang.String类int indexOf(String)方法
-XX:CompileCommand="exclude,java/lang/String.indexOf,(Ljava/lang/String;)I"
3. 设置编译器跳过编译所有类的indexOf方法
-XX:CompileCommand=exclude,*.indexOf