Java對象的記憶體結構
對象記憶體結構
在64位作業系統下,
MarkWord(下圖_mark)占64位
KlassWord(下圖_klass)占32位 64位系統的Klass Word不是32位,預設64位,開啟指針壓縮後為32(感謝評論老哥的指出)
64位系統的Klass Word不是32位,預設64位,開啟指針壓縮後為32
_lengh(隻有資料對象才有,不考慮)
執行個體資料(下圖instance data)看參數的類型,int就占32位(4byte)
補齊(padding)是JVM規定java對象記憶體必須是8byte的倍數,如果執行個體資料占2byte,那麼(64bit的Markword+32bit的Klassword+執行個體資料32bit)=128bit=16byte是8byte的倍數,是以padding部分為0。
檢視對象記憶體結構
JDK8
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.9</version>
</dependency>
public class SynchronizedDemo {
public static void main(String[] args) {
Dog dog = new Dog();
System.out.println(ClassLayout.parseInstance(dog).toPrintable());
}
}
class Dog {
char age;
}
如上圖所示,對象頭中的MarkWord占8byte,KlassWord占4個byte,執行個體屬性age是char類型占2個byte,那麼此時加起來為14byte,為了滿足是8的倍數,要補充2個byte。
下圖是當Dog對象裡的age變為int時列印的結果,請自行對比。
對象頭
下圖是引自《深入了解Java虛拟機:JVM進階特性與最佳實踐(第3版) 周志明》中的一個圖,下圖是32作業系統下的對象頭中的Mark Word(32位),Klass Word(32位),一共是64位。
64作業系統下,Mark Word的長度是64,在加Klass Word(32位),一共是96位,其實對象頭長什麼樣其實不是本文的重點,本文的重點是驗證鎖更新的過程,是以我們隻需要關注對象頭中Mark Word的最後3位即可,如下圖中的後3位。
鎖更新的過程
鎖狀态 | 25bit | 4bit | 1bit | 2bit | ||
23bit | 2bit | 是否偏向鎖 | 鎖标志位 | |||
1 | 無鎖 | 對象的HashCode | 分代年齡 | 01 | ||
2 | 無鎖 | 對象的HashCode | 分代年齡 | 1 | 01 | |
3 | 偏向鎖 | 線程ID | Epoch | 分代年齡 | 1 | 01 |
4 | 輕量級鎖 | 指向棧中鎖記錄的指針 | 00 | |||
5 | 重量級鎖 | 指向重量級鎖的指針 | 10 | |||
6 | GC标記 | 空 | 11 |
前提
由于大小端引起的問題,使得這裡展示的高低位相反,如下圖所示,是以我要關注的就是⑧位置的最後3位足矣。
無鎖狀态
public class SynchronizedDemo {
public static void main(String[] args) {
Dog dog = new Dog();
System.out.println(ClassLayout.parseInstance(dog).toPrintable());
}
}
如下圖所示,001表示的無鎖狀态并且不允許偏向 (其實預設是開啟偏向的,隻不過虛拟機後在運作後幾秒才開啟偏向鎖)
使用下面的參數,如下圖所示 ,會發現狀态為101,表示無鎖狀态
-XX:BiasedLockingStartupDelay=0
由無鎖狀态---->偏向鎖狀态
單線程通路鎖的時候,鎖由無鎖狀态變為偏向鎖狀态。
// -XX:BiasedLockingStartupDelay=0
public class SynchronizedDemo {
public static void main(String[] args) {
Dog dog = new Dog();
System.out.println(ClassLayout.parseInstance(dog).toPrintable());
//上鎖
synchronized (dog){
System.out.println(ClassLayout.parseInstance(dog).toPrintable());
}
System.out.println(ClassLayout.parseInstance(dog).toPrintable());
}
}
class Dog {
int age;
}
如上圖所示,開始狀态為101,為可偏向,無鎖狀态
上鎖後狀态是101,為可偏向,有鎖狀态
解鎖後:狀态為101,為可偏向,有鎖狀态
差別為:當線程給無鎖狀态的lock加鎖時,會把線程ID存儲到MarkWord中,即鎖偏向于該ID的線程,偏向鎖不會自動釋放。
上面表格中2->3的過程。
偏向鎖狀态---->輕量級鎖狀态
多線程使用鎖(不競争,錯開時間通路),鎖由偏向鎖狀态變為輕量級鎖狀态
// -XX:BiasedLockingStartupDelay=0
public class SynchronizedDemo {
public static void main(String[] args) {
Dog dog = new Dog();
System.out.println("初始狀态:");
System.out.println(ClassLayout.parseInstance(dog).toPrintable());
new Thread(
() -> {
synchronized (dog) {
System.out.println("hello world");
}
},
"t1")
.start();
System.out.println("線程1釋放鎖後:");
System.out.println(ClassLayout.parseInstance(dog).toPrintable());
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
e.printStackTrace();
} finally {
}
new Thread(
() -> {
synchronized (dog) {
System.out.println("線程2上鎖:");
System.out.println(ClassLayout.parseInstance(dog).toPrintable());
}
System.out.println("線程2釋放鎖:");
System.out.println(ClassLayout.parseInstance(dog).toPrintable());
},
"t2")
.start();
}
}
class Dog {
int age;
}
初始狀态為101,為可偏向,并且為無鎖狀态
線程1釋放鎖後,狀态為101,并且存儲了線程ID,為偏向鎖狀态,偏向于線程1
線程2上鎖,上鎖後,狀态為00,輕量級鎖狀态
線程2釋放鎖後,狀态為001,此時為不可偏向的無鎖狀态。
重量級鎖狀态
// -XX:BiasedLockingStartupDelay=0
public class SynchronizedDemo {
public static void main(String[] args) {
Dog dog = new Dog();
System.out.println("初始狀态:");
System.out.println(ClassLayout.parseInstance(dog).toPrintable());
new Thread(
() -> {
synchronized (dog) {
System.out.println("");
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
},
"t1")
.start();
new Thread(
() -> {
synchronized (dog) {
System.out.println("線程2上鎖");
System.out.println(ClassLayout.parseInstance(dog).toPrintable());
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
System.out.println("線程2釋放鎖");
System.out.println(ClassLayout.parseInstance(dog).toPrintable());
},
"t2")
.start();
}
}
class Dog {
int age;
}
如上圖所示,鎖初始狀态為101,可偏向無鎖狀态
當線程1在使用鎖,而線程2去上鎖的時候,狀态已經變為010,不可偏向重量級鎖。
總結
單線程使用鎖的時候為偏向鎖。
多線程無競争(錯峰使用鎖)的時候為輕量級鎖。
有競争的時候為重量級鎖。