jol查看對象頭來分析一波synchronize關鍵字加鎖過程
synchronize在jdk1.5之前就是一個重量級的鎖,是在jvm層面加鎖的一種形式,在字節碼運行的過程中被翻譯成了兩個指令,速度很慢。jkd1.6開始對synchronize進行了非常多的優化,使sync有了一個鎖升級機制,可以讓sync在不同的場景下加不同的鎖,大大提升了sync的效率。sync一共有三種鎖狀態:偏向鎖、輕量級鎖、重量級鎖。下面通過查看class對象頭的方式來查看這幾個鎖狀態是怎麼表現的。
import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.vm.VM;
import static java.lang.System.out;
public class JOLExample1 {
public static void main(String[] args) throws Exception {
A a = new A();
out.println(VM.current().details());
out.println(ClassLayout.parseInstance(a).toPrintable());
}
}
# Running 64-bit HotSpot VM.
# Using compressed oop with 3-bit shift.
# Using compressed klass with 3-bit shift.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
com.wave.test.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
通過查看value可以發現沒有加鎖的對象的狀態是01(無鎖狀態)。
偏向鎖在對象頭上有一個bit用來標識偏向狀態,所以如果是101就是偏向鎖
import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.vm.VM;
import static java.lang.System.out;
//沒有競爭,理論上是偏向鎖
public class JOLExample2 {
static A a;
public static void main(String[] args)throws Exception{
a = new A();
out.println("befre lock");
out.println(ClassLayout.parseInstance(a).toPrintable());
sync();
out.println("after lock");
out.println(ClassLayout.parseInstance(a).toPrintable());
}
public static void sync() throws InterruptedException {
synchronized (a){
System.out.println("我也不知道要打印什麼");
}
}
}
神奇的發現加了鎖還是01,這裏是因爲偏向鎖會有一個4s的延遲,修改虛擬機參數或者睡眠5s就可以看到101了(XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0)
befre lock
com.wave.test.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
我也不知道要打印什麼
after lock
com.wave.test.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
一個對象如果被兩個線程加了鎖,那就會變成輕量級鎖,如果被兩個線程競爭了,就會變成重量級鎖
//輕量級鎖
public class JOLExample3 {
static A a;
public static void main(String[] args)throws Exception{
//Thread.sleep(5000);
a = new A();
out.println("befre lock");
new Thread(()->{
sync();
}).start();
Thread.sleep(10000);//如果減小這個值,讓兩個線程發生競爭,就會產生重量級鎖
out.println("after lock");
//out.println(ClassLayout.parseInstance(a).toPrintable());
sync();
}
public static void sync() {
synchronized (a){
try {
out.println(ClassLayout.parseInstance(a).toPrintable());
}catch (Exception e){
}
}
}
}