springboot實戰電商項目mall4j (https://gitee.com/gz-yami/mall4j)java
java開源商城系統git
jdk版本:1.8spring
系統:window10 64位數組
jvm 啓動參數:-XX:BiasedLockingStartupDelay=0 (取消延遲加載偏向鎖)springboot
首先須要已知幾個概念jvm
若是是 array 對象,則會再佔用一個 length 空間(4 字節),記錄數組的長度。工具
3.synchronized 四鎖升級流程以及什麼時候升級(不贅述)編碼
經過實際編碼查看分別在這四個鎖狀態時,鎖標誌位是否相應變化spa
引入 jol 工具包線程
<dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <version>0.9</version> </dependency>
無鎖態
@Test public void test01() throws Exception { Object o = new Object(); System.out.println(ClassLayout.parseInstance(o).toPrintable()); }
執行結果
java.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 00 00 00 (00000101 00000000 00000000 00000000) (5) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
第1、二行的 object header 是 markword 的內存,第三行的 object header 是 class pointer 的內容,第四行是對齊填充。
只需關注第1、二行。
第1、二行由於是由低位打印到高位的,因此須要反過來看纔會和上方的鎖狀態表格中一一對應。
即 <u>00000000 00000000 00000000 00000000 00000000 00000000 000000</u>00 00000101。
對照表格,重點最後三位,是否偏向鎖爲1,鎖標誌位爲01,理論上來講偏向鎖標記應該爲0,這是由於咱們加了個 取消延遲加載偏向鎖的啓動參數致使的,若是把啓動參數去掉,那麼偏向鎖標誌位就是0。
JVM啓動時會進行一系列的複雜活動,好比裝載配置,系統類初始化等等。在這個過程當中會使用大量synchronized關鍵字對對象加鎖,且這些鎖大多數都不是偏向鎖。爲了減小初始化時間,JVM默認延時加載偏向鎖,而咱們把它禁用掉了,因此偏向鎖標誌位就變成了 1。
即使如此,依舊能夠看出下劃線部分爲偏向線程的 id 存儲位置,目前全爲 0,也即這個對象目前不偏向任何線程,因此當前對象仍是可偏向的狀態
即當前對象鎖狀態爲無鎖態得證。
偏向鎖態
@Test public void test2() throws Exception { Object o = new Object(); synchronized (o) {} System.out.println(ClassLayout.parseInstance(o).toPrintable()); }
執行結果
java.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 e8 d1 02 (00000101 11101000 11010001 00000010) (47310853) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
同樣先把前兩行按表格中的順序先排列好,以下:
<u>00000000 00000000 00000000 00000000 00000010 11010001 111010</u>00 00000101
這回能夠看出偏向鎖標誌位1,鎖標誌位爲01,並且偏向鎖的線程 id 也被佔用了,因此顯然該對象線程是偏向鎖態。
同時,從代碼中看出,是先對 o 上 synchronized 鎖且鎖釋放以後纔對 o 進行打印的,能夠得出一個結論,偏向鎖狀態不會主動撤銷,而是會繼續保留其狀態。
輕量級鎖
@Test public void test3() throws Exception { Object o = new Object(); synchronized (o) {} new Thread(() -> { synchronized (o) { System.out.println(ClassLayout.parseInstance(o).toPrintable()); } }).start(); TimeUnit.SECONDS.sleep(10); }
執行結果
java.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 00 ef a5 1e (00000000 11101111 10100101 00011110) (514191104) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
00000000 00000000 00000000 00000000 00011110 10100101 11101111 00000000
對照表,顯然此時的 o 處於輕量級鎖態。由於主線程先對 o 上鎖,o處於偏向鎖,而後再來個線程對 o 上鎖,上鎖前就偏向鎖就會膨脹爲輕量級鎖。
綜上,得證。
重量級鎖
@Test public void test4() throws Exception { Object o = new Object(); AtomicInteger index = new AtomicInteger(0); for (int i = 0; i < 10; i++) { new Thread(() -> { synchronized (o) { index.getAndIncrement(); if(index.get() == 9) System.out.println(ClassLayout.parseInstance(o).toPrintable()); } }).start(); } TimeUnit.SECONDS.sleep(10); }
執行結果
java.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) fa 05 d7 1c (11111010 00000101 11010111 00011100) (483853818) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
00000000 00000000 00000000 00000000 00011100 11010111 00000101 11111010
出現大量鎖競爭,理論上 o 應當處於重量級鎖態,對照表格顯然得出是 o 處於重量級鎖態,得證。
再來看以下兩種狀況
在對 o 上鎖前計算 o 的 hashcode,上鎖時 o 處於什麼狀態
@Test public void test5() throws Exception { Object o = new Object(); o.hashCode(); synchronized (o) { System.out.println(ClassLayout.parseInstance(o).toPrintable()); } }
從理論上將應該仍是偏向鎖,那麼看執行結果。
java.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) b8 e4 ac 02 (10111000 11100100 10101100 00000010) (44885176) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
此時的鎖標誌位爲 00,爲輕量級鎖,並非偏向鎖。
結論:計算過 hashcode 的對象,在上鎖後會直接膨脹爲輕量級鎖,跳過偏向鎖。
在對 o 上鎖後計算 o 的hashcode,此時的 o 處於什麼狀態
@Test public void test6() throws Exception { Object o = new Object(); synchronized (o) { o.hashCode(); System.out.println(ClassLayout.parseInstance(o).toPrintable()); } }
理論上這裏的 o 應該是偏向鎖。看執行結果:
java.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) ba c7 52 1c (10111010 11000111 01010010 00011100) (475187130) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
此時的鎖標誌位爲 10,爲重量級鎖,並非偏向鎖。
結論:處於偏向鎖態的對象只要計算過 hashcode,會直接膨脹爲重量級鎖。
拓展:
檢驗代碼中計算出來的 hashcode 與 markword 中對應記錄 hashcode 中的值一致
@Test public void test7() throws Exception { Object o = new Object(); System.out.println(o.hashCode()); System.out.println(ClassLayout.parseInstance(o).toPrintable()); }
輸出結果:
1174290147 java.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 e3 3e fe (00000001 11100011 00111110 11111110) (-29433087) 4 4 (object header) 45 00 00 00 (01000101 00000000 00000000 00000000) (69) 8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
將 markword 抄寫出來,即 00000000 00000000 00000000 01000101 11111110 00111110 11100011 00000001
對照文章開頭的表格,可知從第26位開始到底56位都是記錄 hashcode 的比特位。即 1000101 11111110 00111110 11100011,將其轉爲十進制,結果爲 1,174,290,147。和代碼中輸出的結果一致。