JVM建立物件及訪問定位過程詳解
阿新 • • 發佈:2020-12-09
1.物件的建立
- 虛擬機器接收到new指令時,檢查這個指令能否在常量池中定位到一個類的符號引用,並且檢查這個符號引用代表的類是否已被載入、解析和初始化。如果都沒有,先執行類載入過程。
- 在類載入通過後,虛擬機器為新物件分配記憶體(把一塊確定大小的記憶體從Java堆中劃分出來),記憶體大小在類載入完成後即可完全確定。
- 兩種分配方式:
- (1):指標碰撞:假設Java堆中記憶體是絕對規整的,即使用過的記憶體在一邊,空閒的記憶體在另外一邊,中間放著一個指標作為指示器,通過移動指標實現記憶體分配。
- (2):空閒列表:如果Java堆中的記憶體並不是規整的,即已使用的記憶體和空閒的記憶體相互交錯,虛擬機器就必須維護一個列表,記錄哪些記憶體塊是可用的,通過從列表中尋找空間劃分給物件例項來分配記憶體。
- Java堆是否規整由所採用的垃圾收集器是否有壓縮整理功能決定。
- 在虛擬機器中建立物件不是執行緒安全的行為,可能出現在給物件A分配記憶體,指標還沒來得及修改,物件B又使用了原來的指標來分配記憶體。有兩種解決方案:
- (1):對分配記憶體空間的動作進行同步處理,實際上虛擬機器採用CAS配上失敗重試的方式保證更新操作的原子性;
- (2):把記憶體分配的動作按照執行緒劃分在不同的空間中進行,即每個執行緒在Java堆中預先分配一小塊記憶體,稱為本地執行緒分配緩衝(Thread Loal Allocation Buffer,TLAB)。
- 記憶體分配完成後,需要將分配到的記憶體空間都初始化為零值,保證物件的例項欄位在Java程式碼中可以不賦初始值就可以直接使用,程式能訪問到這些欄位的資料型別對應的零值。
- 設定物件,把物件是哪個類的例項,如何才能找到類的元資料資訊,物件的雜湊碼,物件的GC分代年齡等存放在物件頭中。
2. 物件的記憶體佈局:物件在記憶體中儲存的佈局可以分為3塊:物件頭(Header)、例項資料(Instance Data)、對齊填充(Padding)。物件頭,包括兩部分資訊:
(1):儲存物件自身的執行時資料,如雜湊碼、GC分代年齡、鎖狀態標誌、執行緒持有的鎖、偏向執行緒ID、偏向時間戳等,這部分資料的長度在32位和64位的虛擬機器中分別為32bit和64bit,官方稱為Mark Word(非固定的資料結構,根據物件的狀態複用自己的儲存空間)。
(2):型別指標,即指向物件的類元資料的指標,虛擬機器通過這個指標來確定這個物件是哪個類的例項。
- 例項資料:物件真正儲存的有效資訊,即程式程式碼中所定義的各種型別的欄位內容。無論是從父類繼承下來的,還是子類自己定義的,都需要記錄。
- 對齊填充:不是必然存在,起著佔位符的作用,由於HotSpot VM要求物件的大小必須是8位元組的整數倍,而物件頭部分正好是8位元組的整數倍,因此當例項資料沒有對齊時,通過對齊填充來補全。
3. 物件的訪問定位:Java通過棧上的reference資料(區域性變量表中的物件引用)來操作堆上的具體物件,reference只規定了指向物件的引用,沒有定義怎麼去定位,訪問堆中的物件的位置。物件訪問方式由迅疾實現。
(1):控制代碼訪問:Java堆會劃分出一塊記憶體作為控制代碼池,reference儲存的就是物件的控制代碼地址,控制代碼包含了物件例項資料和型別資料各自的地址資訊。
優勢:reference中儲存的是穩定的控制代碼地址,物件移動時只改變控制代碼中的例項資料指標,不改變reference。
(2):直接指標:reference中儲存的直接就是物件地址,Java堆中放置訪問物件型別資料(存放在方法區)的地址。
優勢:速度更快,節省了一次指標定位的時間開銷,HotSpot是使用直接指標訪問。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。