1. 程式人生 > >JVM(八):Java 物件模型

JVM(八):Java 物件模型

JVM(八):Java 物件模型

本文將學習物件是如何建立的,物件的記憶體佈局,以及如何定位訪問一個物件。

物件建立

當虛擬機器碰到一個new指令時,首先檢查指令引數能否在常量池中定位一個類的符號引用,並且檢查該符號引用對應的類是否已經被載入,解析和初始化。當一切都確定完成後,JVM就會為其分配記憶體(需要分配的記憶體大小在現在就已經確定,在 下面 中詳細講述)。

物件的記憶體分配方式分為以下兩種:

  1. 指標碰撞,這種分配方式建立在堆內已用空間和剩餘空間是完整的,這樣的話,在兩者之間放置一個指標作為分界點的指示器即可,在分配空間時,只需要移到一下指標位置就好了。
  2. 空閒列表,如果 JVM 內的空間不是規整的,那麼就只能採用此方案了。此時 JVM 會維護一個列表,記錄了哪些記憶體塊是可用的,在分配的時候劃一個大小足夠的區域給物件例項,並更新列表即可。

以上兩種方式採取哪種,取決於 Java 堆是否工整,而堆是否工整又取決於垃圾回收演算法是否具有整理功能。

物件模型

前面說到物件在建立時就已經確定了記憶體大小,那麼 JVM 是怎麼確定物件的大小呢?物件在記憶體中又是如何儲存的呢?

在 JVM 中 Java 的物件模型分為以下3塊,物件頭,例項資料,對齊填充,下面就讓我們來分別介紹一下。

物件頭

物件頭的資料包括兩部分。一部分是用於儲存自身執行時資料,這部分資料被官方稱為“Mark World”。其中儲存資料包括Hashcode、GC 分代年齡、鎖狀態標誌、執行緒持有鎖、偏向執行緒ID、偏向時間戳等等。

物件頭的另外一部分是 型別指標,即物件指向其類元資料的指標。通過這個指標,我們就可以知道該例項屬於哪個類。

例項資料

例項資料就是物件真正儲存的有效資訊,也就是程式碼中定義的各種型別的欄位內容,不論是父類的還是子類的,都需要記錄下來。其儲存順序受到虛擬機器分配策略和定義順序影響。

對齊填充

物件填充不是必要資料。在模型中只是起到佔位符的作用。因為 HotSpot 要求物件起始地址必須是8的整數倍,這樣在例項資料達不到要求的時候,就需要通過對齊填充來補齊。

物件訪問

物件訪問的方式是通過引用來定位、訪問。但 JVM 規範並沒有強制要求該通過何種方式使用引用,因此具體實現還是要依賴與具體虛擬機器型別。

不過目前的主流訪問方式就是以下兩種。

  1. 使用引用。其在 Java 堆中會獨立建立一個控制代碼池,引用指向控制代碼,而控制代碼指向例項資料和型別資料。

使用這種方式來訪問的優點是穩定,例如在 GC 後,例項資料需要移動,那麼只需要修改控制代碼池中的內容即可,reference 指向的是穩定的位置,缺點是這種方式需要二次定位,速度較慢。

  1. 直接指標訪問,引用直接堆中物件地址,堆中儲存了例項資料和型別資料指標,指標直接指向另外儲存的型別資料。

使用這種方式的優點是訪問例項資料快,因為 reference 指向直接的物件,省去了一次記憶體定位開銷。但缺點就是不夠穩定,在物件移動後,reference 也需要修改值。

具體採用何種,不同的虛擬機器有不同的實現,因為兩者各有千秋,並沒有強烈的優缺點,因此不同情況不同處理即可。

總結

在本文中介紹了物件的本質模型是什麼,以及物件是如何建立和訪問使用的,與上文的 JVM 記憶體模型結合來看,可以讓我們瞭解記憶體洩露產生的原因,有助於高效地理解使用 Java 的自動記憶體管理機制。

文章在公眾號“iceWang”第一手更新,有興趣的朋友可以關注公眾號,第一時間看到筆者分享的各項知識點。謝謝!筆芯!

本系列文章主要借鑑自《深入分析 Java Web 技術內幕》和《深入理解 Java 虛擬機器-JVM 高階特性與最佳實踐》。