深入理解Java虛擬機第二章讀書筆記:HotSpot虛擬機在Java堆上的對象分配

1.對象的創建

Step1:檢查

虛擬機在遇到一個new指令時,先檢查這個指令的參數是否能在常量池中定位到一個類的符號引用。然後進行類加載檢查。(涉及到類的加載機制)

Step2:爲對象分配內存空間

在進行完類加載檢查以後,開始爲對象分配內存空間,大致有如下兩種分配方式:

  1. 指針碰撞(要求堆中的內存絕對規整)
  2. 空閒列表(適用於堆中的內存不規整)

第一種:指針碰撞

當堆中的內存絕對規整的時候,將所有空閒的內存放在一邊,所有使用過的內存放在另一邊。中間使用一個指針來當作分界線的指示器,當創建一個新的對象時,指針向空閒的那一邊移動大小相等的一段距離

第二種:空閒列表

當堆中的內存不規整的時候,虛擬機維護一張內存使用表,記錄下來哪些內存是已經被使用過的,哪些還未被使用,在給新的對象分配空間時,會在列表中找一個大小足夠的內存空間分配給這個新創建的對象。

總結:

選擇哪種內存分配方式取決於Java堆是否規整,而Java堆是否規整又取決於虛擬機所採用的垃圾收集期是否帶有壓縮整理功能。

本地線程分配緩衝區(TLAB):

對象的創建以及內存空間分配在Java堆中是很頻繁的行爲,中間可能會存在線程不安全的行爲。

例如線程A創建創建了一個對象,正在分配內存空間,指針還沒來得及向空閒的一邊移動,線程B又創建了一個對象,來分配內存空間。這個過程中不能保證更新操作的原子性。

(什麼叫做操作的原子性?我的理解如下:分子是由多個原子構成,就例如一個操作由多個步驟組成,那麼只涉及到一步操作的就是原子操作。比如x++就不是一個原子操作,它要先取x的值,然後將x的值賦予出去,最後再加1,這中間涉及到多個步驟,所以這就不是原子操作。x=1是一個原子操作,因爲它只有一步操作,那就是將1賦值給x)

這裏的本地線程分配緩衝區其實就是一開始就在Java堆中給每個線程分配一個私有的內存空間。由這個線程所創建的對象,就在這個線程私有的分配緩衝區內進行內存空間分配。這樣就不存在上述的不能保證更新操作的原子性這一問題。

只有分配緩衝區的內存空間用完以後,才需要同步鎖定。虛擬機是否使用分配緩衝區,可以通過-XX:+/-UseTLAB參數來設定。

Step3:內存空間初始化爲零

內存分配完成後,虛擬機要將分配的這塊內存空間歸零,如果使用了TLAB,這一步操作也可以提前到TLAB分配時進行。

作用:保證了對象的實例字段在Java代碼中可以不賦初值就進行使用,程序能訪問到這些字段的數據類型所對應的零值。

Step4:對對象進行設置

虛擬機要對對象進行必要的設置,例如這個對象是哪個類的實例、怎麼才能找到類的元數據信息、對象的哈希碼、對象的GC分代年齡信息等。

Step5:最後一步,執行init()方法

從虛擬機的角度來看,對象的創建已經完成了,但是從程序的角度來看,對象的創建纔剛剛開始,因爲這個時候只是執行了new指令,對象中的所有字段都還是0,還沒有執行init()方法,init()方法是按照程序員的意願,將對象初始化成一個真正可用的對象。

 

 

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