一文搞懂JVM--HotSpot虛擬機對象


本篇主要探討HotSpot虛擬機在java堆中對象分配、佈局和訪問的全過程。我們可以把全過程看做執行一條new指令的整個過程。
在這裏插入圖片描述

java對象創建過程

檢查類加載

虛擬機遇到一條new指令時,首先將去檢查這個指令的參數是否能在常量池中定位到一個類的符號的引用,並且檢查這個符號引用代表的類是否已經被加載、解析和初始化過。如果沒有那必須先執行相應的類加載過程。類加載過程下一篇爲大家講解。

爲新生對象分配內存

類加載檢查通過後,虛擬機將爲新生對象分配內存。對象所需內存的大小在類加載完成後便可以確定,爲對象分配空間的任務等同於把一塊確定大小的內存從java堆中劃分出來。
在劃分內存的時候根據內存是否是絕對規整,有兩種劃分形式:指針碰撞和空閒列表。Java堆是否規整由所採用的垃圾收集器是否帶有壓縮整理功能決定。通常使用Serial、ParNew等帶Compact過程的收集器時,系統採用的分配算法是指針碰撞;使用CMS這種基於Mart-Sweep算法的收集器時,通常採用空閒列表。

(1)指針碰撞

假如Java堆內存是絕對規整的,所有用過的內存都放在一邊,空閒的內存放在另一邊,中間放着一個指針作爲分界點的指示器,那所分配內存就僅僅是把那個指針想空閒空間那邊挪動一段與對象大小相等的距離,這種分配方式成爲“指針碰撞”。

(2)空閒列表

如果Java堆中的內存並不是規整的,已使用的內存和空閒的內存相互交錯,這時候就沒有辦法使用“指針碰撞”,虛擬機就必須維護一個列表,記錄上哪些內存塊是可用的,在分配的時候從列表中找到一塊足夠大的空間劃分給對象實例,並更新列表上的記錄,這種分配方式成爲“空閒列表”。


有了可用空間還需要考慮另一個問題:對象創建在虛擬機中是非常頻繁的行爲,即使是僅僅修改一個指針所指向的位置,在併發情況下也並不是線程安全的,這種情況有兩種解決方案:

①併發問題–CAS

對分配內存空間的動作進行同步處理,虛擬機採用CAS配上失敗重試的方式保證更新操作的原子性。

CAS:比較交換(compare and swap)是一種有名的無鎖算法、CAS的工作原理:需要3個操作數,分別是內存位置(在java中可以簡單理解爲變量的內存地址,用V表示)、舊的預期值(用A表示)和新值(用B表示)。CAS指令執行時,當且僅當V符合舊預期值A時,處理器用新值B更新V的值,否則他就不執行更新,但是無論是否更新了V的值,都會返回V的舊值,刪除的處理過程是一個原子操作。

②併發問題-TLAB

把內存分配的動作按照線程劃分在不同的空間之中進行,即每個線程在java堆中預先分配一小塊內存,稱爲本地線程緩衝(Thread Local Allocation Buffer TLAB)。哪個線程要分配內存,就在那個線程的TLAB上分配,只有TLAB用完並分配新的TLAB時,才需要同步鎖定。虛擬機是否使用TLAB,可以通過-XX:+/-UseTLAB參數設定。

初始化爲零值

內存分配完後,虛擬機需要將分配到的內存空間都初始化爲零值(不包括對象頭),如果使用TLAB,這一工作也可以提前到TLAB分配時進行。

設置對象頭

接下來,虛擬機要對對象進行必要的設置,這部分信息都存放在對象的對象頭之中。

執行init方法

在上面工作都完成之後,從虛擬機的視角來看,一個新的對象已經產生了,但從java程序的視角來看,對象創建纔剛剛開始——方法還沒有執行,所有的字段都還爲零。所以,一般來說(由字節碼中是否跟隨invokespecial指令所決定),執行new指令之後會接着執行方法,把對象按照程序員的意願進行初始化,這樣一個真正可用的對象纔算完全產生出來。

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