为什么Java编译器在“同步块”之前添加“冗余读取”?

use*_*415 2 java bytecode synchronized

// the java source code
public class Demo {
    private final Object lock = new Object();

    public void read() {
        synchronized (lock) {
            // more code here ...
        }
    }
}

// the decompiled .class file
public class Demo {
    private final Object lock = new Object();

    public void read() {
    // Why Java compiler add this line? Is the 'read this.lock' redundant?
    Object var1 = this.lock;
    synchronized(this.lock) {
        // more code here ...
    }
    }
}
Run Code Online (Sandbox Code Playgroud)

字节码在这里javap -l -p -s demo.class

public void read();
descriptor: ()V
flags: ACC_PUBLIC
Code:
  stack=2, locals=3, args_size=1
     0: aload_0
     1: getfield      #3                  // Field lock:Ljava/lang/Object;
     4: dup
     5: astore_1
     6: monitorenter
     7: aload_1
     8: monitorexit
     9: goto          17
    12: astore_2
    13: aload_1
    14: monitorexit
    15: aload_2
    16: athrow
    17: return
  Exception table:
     from    to  target type
         7     9    12   any
        12    15    12   any
  LineNumberTable:
    line 15: 0
    line 16: 7
    line 17: 17
  LocalVariableTable:
    Start  Length  Slot  Name   Signature
        0      18     0  this   Lxechoz/vipshop/com/demo/thread/Demo;
  StackMapTable: number_of_entries = 2
    frame_type = 255 /* full_frame */
      offset_delta = 12
      locals = [ class xechoz/vipshop/com/demo/thread/Demo, class java/lang/Object ]
      stack = [ class java/lang/Throwable ]
    frame_type = 250 /* chop */
      offset_delta = 4
Run Code Online (Sandbox Code Playgroud)

我认为线 1: getfield #3 // Field lock:Ljava/lang/Object;

对应于Object var1 = this.lock;

我知道编译器会通过添加或删除一些代码来优化代码。

但是,为什么编译器在synced块之前添加read语句。

为什么需要这个?还是为什么要进行优化

Sub*_*mal 5

JLS 3.14开始

Java虚拟机中的同步是通过监视器的进入和退出来实现的,显式地(通过使用monitorenter和monitorexit指令)或隐式地(通过方法调用和返回指令)来实现。

为确保monitorexit始终执行,编译器为添加了一个隐式catch子句Throwable

为了在突然的方法调用完成时强制正确配对Monitorenter和MonitorExit 指令,编译器生成异常处理程序(第2.10节),该异常处理程序将匹配任何异常,并且其关联代码执行必要的MonitorExit指令。

使用时,javap -c Demo您可以在偏移量12-16处看到此附加字节码

 0: aload_0
 1: getfield      #3                  // Field lock:Ljava/lang/Object;
 4: dup
 5: astore_1
 6: monitorenter
 7: aload_1 
 8: monitorexit
 9: goto          17
12: astore_2
13: aload_1
14: monitorexit
15: aload_2
16: athrow
17: return
Exception table:
     from    to  target type
         7     9    12   any
        12    15    12   any
Run Code Online (Sandbox Code Playgroud)

生成的代码为伪代码

Object var1 = this.lock;
try {
   monitorenter(var1);
   // more code here ...
   monitorexit(var1);
} catch (Throwable t) {
   monitorexit(var1);
   throw t;
}
Run Code Online (Sandbox Code Playgroud)