1. 程式人生 > >JVM層次理解物件建立、記憶體佈局及訪問定位

JVM層次理解物件建立、記憶體佈局及訪問定位

物件的建立

物件的建立,當虛擬機器接收到new指令,會去檢查這個指令的引數是否能在常量池中定位到一個類的符號引用,並且檢查該引用是否已經被載入、解析和初始化過,如果沒有則必須經歷該過程。當類載入檢查通過後,物件建立所需的堆記憶體空間在類載入期間就已經完全確定,接下來在堆中以指標碰撞、空閒表格等方式來劃下物件在堆中的儲存塊。然後將虛擬機器會將物件分配到的記憶體空間都初始化為零。最後將物件進行必要的設定,比如該物件是哪個類的例項、如何才能找到類的元資料資訊、物件的雜湊碼、物件的GC分代年齡。

物件記憶體佈局

上一部分我們講到了物件的物件頭,物件頭是什麼東西呢?下面我們講解下物件在記憶體中的佈局。以HotSpot虛擬機器為例,物件在記憶體中的佈局可以分為三部分:物件頭、例項資料、對齊填充。

  • 物件頭 物件頭包括兩部分:第一部分用於儲存物件自身的執行時資料:雜湊碼、GC分代、執行緒持有鎖等資訊;另一部分就是指型別指標,就是物件指向它的類元資料的指標,虛擬機器通過這個指標來判斷這個物件屬於哪個類的例項。java資料物件的話,在頭中還有一塊記錄陣列長度的資料。

  • 例項資料 該部分就是物件真正儲存的有效資料區域,也就是我們在程式碼裡面所定義的各種型別的欄位內容。無論從父類繼承下來的還是在子類中定義的,都需要記錄下來。

  • 對齊填充 不是必然存在的,也沒特別的含義,僅僅是佔位符的作用。由於hotspot的自動記憶體管理系統要求物件起始地址必須是8位元組的整數倍,換句話說,物件的大小必須是8位元組倍數,而物件頭的大小剛好是8位元組的倍數(一倍或者二倍),因此,當物件例項資料部分沒有對齊時,可以通過對齊填充進行補全。

物件訪問定位

物件建立在java堆上,我們如何才能訪問他們呢?其實,java程式需要通過棧上的一個reference欄位資料來操作堆上的具體物件,即棧上的reference欄位指向堆上的具體物件。目前主流的訪問方式有兩種:

  • 使用控制代碼 如果使用控制代碼進行訪問,那麼java堆會劃出一部分記憶體來作為控制代碼池,reference儲存的物件就是控制代碼池中的控制代碼地址。控制代碼中包含了物件例項資料與型別資料各自的具體地址資訊。

    這裡寫圖片描述

  • 直接訪問

    如果是直接訪問,那麼java堆中就要包含一個指向方法區的該物件型別資料的指標(引用),而reference中包含的就直接是物件地址。

    這裡寫圖片描述

以上的這兩種方式來說,使用控制代碼的好處就是reference中儲存的是一個穩定的地址,在物件被移動的過程中,只會更新控制代碼中的例項資料指標,並不會改變reference中的資料;使用直接訪問方式的話,它的訪問速度就是更快。因為減少了一次指標的定位,這樣節省了時間。因為物件的訪問在java堆中十分頻繁,所以這樣的節省操作一直積累也會成為一個很可觀的減少時間成本的操作。