概述:
上一篇文章,介紹了虛擬機器類載入的過程,那麼類載入好之後,虛擬機器下一步該幹什麼呢。我們知道java是物件導向的程式語言,所以物件可以說是java'的靈魂,這篇文章我們就來介紹
虛擬機器是如何建立物件、物件記憶體分配以及物件是如何使用的(訪問定位)。由於各個虛擬機器的實現不盡相同,所以這裡我們以最常用的HotSpot虛擬機器為例來介紹。
物件的建立:
物件在虛擬機器中建立的步驟如下:
- 當虛擬機器遇到一條位元組碼new指令時,首先會去檢查這個類是否被載入、解析和初始化,如果沒有,則執行類載入(類載入步驟這裡就不介紹了,請檢視JVM(三))。
- 類載入檢查通過後,接下來虛擬機器將在堆中為新生物件分配和物件同等大小的記憶體。
- 記憶體分配完之後,虛擬機器會將分配到的記憶體空間(不包括物件頭)都初始化為零值,保證了物件的例項欄位有初始值,使得不賦值也可以使用,只是值為零而已(注意,如果是引用物件則為null)。
- 接下來,java虛擬機器還要對物件進行必要的設定,比如這個物件是哪個類的例項、如何才能找到類的後設資料資訊、物件的雜湊碼、物件的GC分代年齡等資訊。
- 執行Class檔案中<init>()方法,即建構函式,這樣一個真正可用的物件才算完全構建完成。
物件的記憶體佈局:
在HotSpot虛擬機器裡,物件在堆記憶體中的儲存佈局可以劃分為三個部分:物件頭、例項資料和對其填充。
物件頭:
HotSpot虛擬機器物件的物件頭部分包括兩類資訊:
- 第一類是用於儲存物件自身的執行資料,如何雜湊碼(hashcode)、GC分代年齡、鎖狀態標誌、執行緒持有的鎖、偏向執行緒ID、偏向時間戳等,官方稱它為"Mark Word"。
- 物件頭的另外一部分是型別指標,即物件指向它的型別後設資料指標,java虛擬機器通過指標來確定該物件是哪個型別的例項。如果是陣列物件,在物件頭中還有一塊用於記錄陣列長度的資料。
例項資料:
顧名思義,例項資料部分儲存的就是例項物件的相關資訊了,即我們在程式程式碼裡面所定義的各種型別的欄位內容,無論是從父類繼承下來的,還是在子類中定義的欄位都必須記錄起來。
對齊填充:
物件的第三部分是對齊填充,這並不是必然存在的,也沒有特別的含義,它僅僅起著佔位符的作用。由於HotSpot虛擬機器的自動記憶體管理系統要求物件的起始地址必須是8位元組的整數倍,所以
不足8的整數倍就需要補齊。
物件的訪問定位:
建立物件自然是為了後續使用該物件,java虛擬機器規範規定java程式通過虛擬機器棧的reference來操作堆上的具體物件,各個虛擬機器實現的訪問方式也不盡相同,主流的訪問方式主要使用控制程式碼和直接指標兩種,
我們先來看通過控制程式碼的方式訪問,如下圖:
通過控制程式碼訪問,java堆中會將劃分出一塊記憶體來作控制程式碼池,reference中儲存的就是物件的控制程式碼地址,控制程式碼中包含了物件例項資料以及型別資料的地址資訊。而指標訪問的話,
reference中儲存的直接就是物件地址,物件例項中同時還需要存指向物件型別資料的指標。如下圖所示:
這兩種物件訪問方法各有優勢,直接指標訪問方式,雖然訪問訪問速度快,但是垃圾回收的效率沒有控制程式碼池的效率高。我們常用的HotSpot是使用的直接指標的方式訪問物件。