JVM(四)-虛擬機對象

  • 2020 年 12 月 2 日
  • 筆記

概述:

  上一篇文章,介紹了虛擬機類載入的過程,那麼類載入好之後,虛擬機下一步該幹什麼呢。我們知道java是面向對象的程式語言,所以對象可以說是java’的靈魂,這篇文章我們就來介紹

虛擬機是如何創建對象、對象記憶體分配以及對象是如何使用的(訪問定位)。由於各個虛擬機的實現不盡相同,所以這裡我們以最常用的HotSpot虛擬機為例來介紹。

對象的創建:

  對象在虛擬機中創建的步驟如下:

  1. 當虛擬機遇到一條位元組碼new指令時,首先會去檢查這個類是否被載入、解析和初始化,如果沒有,則執行類載入(類載入步驟這裡就不介紹了,請查看JVM(三))。
  2. 類載入檢查通過後,接下來虛擬機將在堆中為新生對象分配和對象同等大小的記憶體。
  3. 記憶體分配完之後,虛擬機會將分配到的記憶體空間(不包括對象頭)都初始化為零值,保證了對象的實例欄位有初始值,使得不賦值也可以使用,只是值為零而已(注意,如果是引用對象則為null)。
  4. 接下來,java虛擬機還要對對象進行必要的設置,比如這個對象是哪個類的實例、如何才能找到類的元數據資訊、對象的哈希碼、對象的GC分代年齡等資訊。
  5. 執行Class文件中<init>()方法,即構造函數,這樣一個真正可用的對象才算完全構建完成。

對象的記憶體布局:

  在HotSpot虛擬機里,對象在堆記憶體中的存儲布局可以劃分為三個部分:對象頭、實例數據和對其填充。

對象頭:

  HotSpot虛擬機對象的對象頭部分包括兩類資訊:

  1. 第一類是用於存儲對象自身的運行數據,如何哈希碼(hashcode)、GC分代年齡、鎖狀態標誌、執行緒持有的鎖、偏向執行緒ID、偏向時間戳等,官方稱它為”Mark Word”。
  2. 對象頭的另外一部分是類型指針,即對象指向它的類型元數據指針,java虛擬機通過指針來確定該對象是哪個類型的實例。如果是數組對象,在對象頭中還有一塊用於記錄數組長度的數據。

實例數據:

  顧名思義,實例數據部分存儲的就是實例對象的相關資訊了,即我們在程式程式碼裡面所定義的各種類型的欄位內容,無論是從父類繼承下來的,還是在子類中定義的欄位都必須記錄起來。

對齊填充:

  對象的第三部分是對齊填充,這並不是必然存在的,也沒有特別的含義,它僅僅起著佔位符的作用。由於HotSpot虛擬機的自動記憶體管理系統要求對象的起始地址必須是8位元組的整數倍,所以

不足8的整數倍就需要補齊。

對象的訪問定位:

  創建對象自然是為了後續使用該對象,java虛擬機規範規定java程式通過虛擬機棧的reference來操作堆上的具體對象,各個虛擬機實現的訪問方式也不盡相同,主流的訪問方式主要使用句柄和直接指針兩種,

我們先來看通過句柄的方式訪問,如下圖:

 

 

  通過句柄訪問,java堆中會將劃分出一塊記憶體來作句柄池,reference中存儲的就是對象的句柄地址,句柄中包含了對象實例數據以及類型數據的地址資訊。而指針訪問的話,

reference中存儲的直接就是對象地址,對象實例中同時還需要存指向對象類型數據的指針。如下圖所示:

 

   這兩種對象訪問方法各有優勢,直接指針訪問方式,雖然訪問訪問速度快,但是垃圾回收的效率沒有句柄池的效率高。我們常用的HotSpot是使用的直接指針的方式訪問對象。