如何在Java中反编译volatile变量?

MrR*_*ROY 7 java jit jvm volatile

我被告知volatile关键字可以在变量的写操作之前添加内存屏障.所以我写代码:

public class Test {
    private Object o;

    public Test() {
        this.o = new Object();
    }

    private volatile static Test t;

    public static void createInstance() {
        t = new Test();             // volatile would insert memory barrier here.
    }

    public static void main(String[] args) throws Exception {
        Test.createInstance();
    }
}
Run Code Online (Sandbox Code Playgroud)

然后反编译它:

Compiled from "Test.java"
public class Test extends java.lang.Object{
public Test();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   aload_0
   5:   new #2; //class java/lang/Object
   8:   dup
   9:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   12:  putfield    #3; //Field o:Ljava/lang/Object;
   15:  return

public static void createInstance();
  Code:
   0:   new #4; //class Test
   3:   dup
   4:   invokespecial   #5; //Method "<init>":()V
   7:   putstatic   #6; //Field t:LTest;
   10:  return

public static void main(java.lang.String[])   throws java.lang.Exception;
  Code:
   0:   invokestatic    #7; //Method createInstance:()V
   3:   return

}
Run Code Online (Sandbox Code Playgroud)

我看不到任何与内存屏障相关的内容,然后我删除volatile并再次反编译,字节代码根本不会改变.

我怎么能在字节码中找到任何东西?

Mar*_*nik 9

在Java规范的层面上不存在内存屏障的概念.它是某些CPU架构的低级实现细节,例如当今最流行的NUMA架构.

因此,您需要查看特定JVM实现中的即时编译器生成的机器代码,例如x86体系结构上的HotSpot.在那里,如果你足够熟练地解释x86机器代码,你会看到内存屏障的表现形式.

  • 谢谢@Marko,尽管内存屏障是由JIT产生的,但我认为字节码中应该包含一些语法以指示变量是易失的,不是吗?字节码与非易失性相同,JVM如何知道有一个易失性?:D (2认同)
  • 它是变量本身的标志,而不是访问变量的代码. (2认同)

Jul*_*ien 8

如果使用javap和正确的选项对其进行测试,则可以看到标志ACC_VOLATILE:

javap  -v -p Test
Run Code Online (Sandbox Code Playgroud)

打印:

 private static volatile Test t;
 flags: ACC_PRIVATE, ACC_STATIC, ACC_VOLATILE
Run Code Online (Sandbox Code Playgroud)

(标志在jvm规范第4章中定义.类文件格式)