9\ JAVA 對象內存結構

  HotSpot虛擬機中,對象在內存中存儲的佈局可以分爲三塊區域:對象頭(Header)、實例數據(Instance Data)和對齊填充(Padding)。

 對象頭

  • markWord
    • 用於存儲對象自身的運行時數據, 如哈希碼(HashCode)、GC分代年齡、鎖狀態標誌、線程持有的鎖、偏向線程ID、偏向時間戳等等,這部分數據的長度在32位和64位的虛擬機(暫 不考慮開啓壓縮指針的場景)中分別爲32個和64個Bits。
    • Mark Word被設計成一個非固定的數據結構以便在極小的空間內存儲儘量多的信息,它會根據對象的狀態複用自己的存儲空間。
      • 無鎖狀  

       

      • 加鎖狀態

                        

    其中輕量級鎖和偏向鎖是Java 6 對 synchronized 鎖進行優化後新增加的,稍後我們會簡要分析。這裏我們主要分析一下重量級鎖也就是通常說synchronized的對象鎖,鎖標識位爲10,其中指針指向的是monitor對象(也稱爲管程或監視器鎖)的起始地址。每個對象都存在着一個 monitor 與之關聯,對象與其 monitor 之間的關係有存在多種實現方式,如monitor可以與對象一起創建銷燬或當線程試圖獲取對象鎖時自動生成,但當一個 monitor 被某個線程持有後,它便處於鎖定狀態。在Java虛擬機(HotSpot)中,monitor是由ObjectMonitor實現的,其主要數據結構如下(位於HotSpot虛擬機源碼ObjectMonitor.hpp文件,C++實現的)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

ObjectMonitor() {

    _header       = NULL;

    _count        = 0//記錄個數

    _waiters      = 0,

    _recursions   = 0;

    _object       = NULL;

    _owner        = NULL;

    _WaitSet      = NULL; //處於wait狀態的線程,會被加入到_WaitSet

    _WaitSetLock  = 0 ;

    _Responsible  = NULL ;

    _succ         = NULL ;

    _cxq          = NULL ;

    FreeNext      = NULL ;

    _EntryList    = NULL ; //處於等待鎖block狀態的線程,會被加入到該列表

    _SpinFreq     = 0 ;

    _SpinClock    = 0 ;

    OwnerIsThread = 0 ;

  }

  ObjectMonitor中有兩個隊列,_WaitSet 和 _EntryList,用來保存ObjectWaiter對象列表( 每個等待鎖的線程都會被封裝成ObjectWaiter對象),_owner指向持有ObjectMonitor對象的線程,當多個線程同時訪問一段同步代碼時,首先會進入 _EntryList 集合,當線程獲取到對象的monitor 後進入 _Owner 區域並把monitor中的owner變量設置爲當前線程同時monitor中的計數器count加1,若線程調用 wait() 方法,將釋放當前持有的monitor,owner變量恢復爲null,count自減1,同時該線程進入 WaitSet集合中等待被喚醒。若當前線程執行完畢也將釋放monitor(鎖)並復位變量的值,以便其他線程進入獲取monitor(鎖)。如下圖所示

 

由此看來,monitor對象存在於每個Java對象的對象頭中(存儲的指針的指向),synchronized鎖便是通過這種方式獲取鎖的,也是爲什麼Java中任意對象可以作爲鎖的原因,同時也是notify/notifyAll/wait等方法存在於頂級對象Object中的原因。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章