在我們學習synchronized的原理之前,必須要先學習下java的對象頭,這有助於我們理解synchronized。
一、對象頭
1.1 對象頭介紹
java對象分爲對象頭,對象體,對齊字段,如下所示:
我們下面主要關注對象頭的內容:
對象頭 = Mark Word + Klass Word
如果是數組對象則:
對象頭 = Mark Word + Klass Word + 數組長度
Klass Word:存儲一個地址,長度取決於系統位數,32位和64位,該地址指向方法區中類的元數據信息。
常見的虛擬機分爲32位和64位,則其對象頭也會不同:
在32位系統中,Mark Word = 4 bytes = 32 bits,對象頭 = 8 bytes = 64 bits;
在64位系統中,Mark Word = 8 bytes = 64 bits ,對象頭 = 16 bytes = 128bits;
bytes 是字節,bits 是位。
- 32位虛擬機虛擬機普通對象頭
|-----------------------------------------------------------|
| Object Header (64 bits) |
|---------------------------------|-------------------------|
| Mark Word (32 bits) | Klass Word (32 bits) |
|---------------------------------|-------------------------|
- 32位虛擬機虛擬機數組對象頭
|---------------------------------------------------------------------------------|
| Object Header (96 bits) |
|--------------------------------|-----------------------|------------------------|
| Mark Word(32bits) | Klass Word(32bits) | array length(32bits) |
|--------------------------------|-----------------------|------------------------|
- 32位虛擬機Mark Word
|----------------------------------------------------------------------------------------------|
| Mark Word(32bits) | State |
|----------------------------------------------------------------------------------------------|
| hashcode:25 | age:4 | biased_lock:0 | lock:01 | Nomal |
|----------------------------------------------------------------------------------------------|
| thread:23 | epoch:2 | age:4 | biased_lock:1 | lock:01 | Biased |
|----------------------------------------------------------------------------------------------|
| ptr_to_lock_record:30 | lock:00 | Lightweight Locked |
|----------------------------------------------------------------------------------------------|
| ptr_to_heavyweight_monitor:30 | lock:10 | Heavyweight Locked |
|----------------------------------------------------------------------------------------------|
| | lock:11 | Marked for GC |
|----------------------------------------------------------------------------------------------|
- 64位虛擬機虛擬機普通對象頭
|--------------------------------------------------------------------------------------------|
| Object Header(128bits) |
|--------------------------------------------------------------------------------------------|
| Mark Word(64bits) | Klass Word(64bits) |
|--------------------------------------------------------------------------------------------|
- 64位虛擬機虛擬機數組對象頭
|---------------------------------------------------------------------------------------------------------|
| Object Header (192 bits) |
|--------------------------------|-----------------------|------------------------|-----------------------|
| Mark Word(64bits) | Klass Word(64bits) | array length(32bits) | alignment(32bits) |
|--------------------------------|-----------------------|------------------------|-----------------------|
在數組對象當中,有一個對齊長度(alignment/padding gap)是8字節,但是其中是包含數組長度4個字節,所以對齊字節長度也是4,具體可以看我下一小節的演示。
- 64位虛擬機Mark Word
|----------------------------------------------------------------------------------------------|
| Mark Word(64bits) | State |
|----------------------------------------------------------------------------------------------|
| unused:25|identity_hashcode:31|unused:1|age:4|biase_lock:0| lock:01 | Nomal |
|----------------------------------------------------------------------------------------------|
| thread:54| epoch:2 |unused:1|age:4|biase_lock:1| lock:01 | Biased |
|----------------------------------------------------------------------------------------------|
| ptr_to_lock_record:62 | lock:00 | Lightweight Locked |
|----------------------------------------------------------------------------------------------|
| ptr_to_heavyweight_monitor:62 | lock:10 | Heavyweight Locked |
|----------------------------------------------------------------------------------------------|
| | lock:11 | Marked for GC |
|----------------------------------------------------------------------------------------------|
1.2 查看對象頭
引入下列依賴:
<!-- https://mvnrepository.com/artifact/org.openjdk.jol/jol-core -->
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.16</version>
</dependency>
本文並沒有使用網上大家都在使用的0.10版本,因爲對比後我發現,新版本0.16更能清晰的體現對象頭的內容。同學們可以自行對比下。
首先聲明兩個JVM的參數:
- -XX:-UseCompressedOops 關閉壓縮
- -XX:+UseCompressedOops 開啓壓縮
我的是默認開啓的,所以需要使用-XX:-UseCompressedOops將其關閉。
下面會簡單模擬是普通對象和數組對象,我的電腦都是64位的,即參照64位虛擬機的結果。
給定一個空的Student對象:
public class Student {
}
測試類如下:
public class ObjectHeader {
public static void main(String[] args) {
//對象
Student student = new Student();
//數組
String[] strings = new String[]{};
// 打印jvm的具體參數
System.out.println(VM.current().details());
// 打印普通對象頭信息
System.out.println(ClassLayout.parseInstance(student).toPrintable());
// 打印數組對象頭信息
System.out.println(ClassLayout.parseInstance(strings).toPrintable());
}
}
結果如下:
Connected to the target VM, address: '127.0.0.1:63960', transport: 'socket'
# Running 64-bit HotSpot VM.
# Objects are 8 bytes aligned.
# Field sizes by type: 8, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 8, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
com.cloud.bssp.thread.objectheader.Student object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000000000000001 (non-biasable; age: 0)
8 8 (object header: class) 0x000000001c2b1cd0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
[Ljava.lang.String; object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000000000000001 (non-biasable; age: 0)
8 8 (object header: class) 0x000000001bfa4268
16 4 (array length) 0
16 8 (alignment/padding gap)
24 0 java.lang.String String;.<elements> N/A
Instance size: 24 bytes
Space losses: 8 bytes internal + 0 bytes external = 8 bytes total
下面我們逐行進行講解:
- JVM參數
// 64位虛擬機
Running 64-bit HotSpot VM.
// 按照8個字節對齊,即忽略第一個8字節
Objects are 8 bytes aligned.
// 忽略8字節後,餘下的其實都是基本類型所佔的字節:boolean,byte,char,short,int,float,long,double的大小
Field sizes by type: 8, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
// 數組中元素所佔字節,同上面
>Array element sizes: 8, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
- 普通對象 com.cloud.bssp.thread.objectheader.Student
// 很明顯,下面是mark word的大小,8個字節,64bits
object header: mark
// 如下是klass word的大小,8個字節,64bits
object header: class
// 實例大小是16字節。
Instance size: 16 bytes
//空間損失,是否有對其字節,內部對齊和外部對齊
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
- 數組對象 [Ljava.lang.String; object
// 很明顯,下面是mark word的大小,8個字節,64bits
object header: mark
// 如下是klass word的大小,8個字節,64bits
object header: class
// 數組長度
array length
// 內部的對齊字節大小
alignment/padding gap
// 空數組
java.lang.String String;.<elements> N/A
// 實例大小是24
Instance size: 24 bytes
//空間損失,8個是內部對齊字節,其中包含字節的長度4,也就是補齊了4個
Space losses: 8 bytes internal + 0 bytes external = 8 bytes total
二、Mark Word簡介
這一節簡單介紹小Mark Word當中的參數都是什麼意思,以64位虛擬機介紹。
|----------------------------------------------------------------------------------------------|
| Mark Word(64bits) | State |
|----------------------------------------------------------------------------------------------|
| unused:25|identity_hashcode:31|unused:1|age:4|biase_lock:0| lock:01 | Nomal |
|----------------------------------------------------------------------------------------------|
| thread:54| epoch:2 |unused:1|age:4|biase_lock:1| lock:01 | Biased |
|----------------------------------------------------------------------------------------------|
| ptr_to_lock_record:62 | lock:00 | Lightweight Locked |
|----------------------------------------------------------------------------------------------|
| ptr_to_heavyweight_monitor:62 | lock:10 | Heavyweight Locked |
|----------------------------------------------------------------------------------------------|
| | lock:11 | Marked for GC |
|----------------------------------------------------------------------------------------------|
先看state這一列,由上至下分別代表synchronized鎖的狀態:無鎖,偏向鎖,輕量級鎖,重量級鎖,GC。每個鎖的含義將在下一篇文章重點講解。
下面以行爲單位,由上至下查看:
Nomal (無鎖)
unused:未使用25位
identity_hashcode:31位的對象標識hashCode
unused:未使用1位
age:4位的Java對象GC回收年齡。
biase_lock:1位偏向鎖標記,0未啓用
locked:2位鎖狀態標誌位,配合biased_lock表示java對象各階段不同鎖狀態。Biased(偏向鎖)
thread:54位的持有偏向鎖的線程id
epoch:偏向鎖的時間戳
unused:未使用1位
age:4位的Java對象GC回收年齡。
biase_lock:1位偏向鎖標記,1啓用
locked:2位鎖狀態標誌位,配合biased_lock表示java對象各階段不同鎖狀態。Lightweight Locked(輕量級鎖)
ptr_to_lock_record:輕量級鎖狀態下,指向棧中鎖記錄的指針。
locked:2位鎖狀態標誌位,00表示輕量級鎖。Heavyweight Locked(重量級鎖)
ptr_to_heavyweight_monitor:重量級鎖狀態下,指向Monitor(監視器或管程,下一篇文章講解)的指針。
locked:2位鎖狀態標誌位,10表示輕量級鎖。Marked for GC(GC標記)
locked:2位鎖狀態標誌位,11表示被標記爲垃圾回收。
關於對象頭的內容,目前就介紹這麼多,下一篇文章會講解synchronized的相關內容和原理。有用的話給個關注,點贊吧!!