對象創建
在語言層面,創建一個對象通常僅一個new關鍵字就可以解決了,但是在虛擬機中,對象的創建要經過一個復雜的過程。
方法區中的常量池
當虛擬機遇到一個new關鍵字時,首先去方法區中的常量池中找有沒有這個類的符號引用,並檢查這個符號代表的類是否已經被加載、解析和初始化過,如果沒有先執行類的加載過程。
堆內存
類加載完成后,接下來在虛擬機中的堆上划分出一塊內存,存儲類的對象(大小在類加載完成后,根據其內部的變量類型與引用就可以確定類需要的空間大小)。由於GC的機制不同,所以在分配時可能存在兩種機制。
空閑列表法
如果垃圾回收機制為標記清除法
那么虛擬機必須維護一個列表,記錄着哪一塊內存可用,再分配時找到一塊足夠大的空間,划分給類對象實例。
指針碰撞法
如果垃圾回收機制為復制算法
或標記整理法
,那么在分配內存時,直接將指針向空閑區域移動一段與對象大小相等的距離即可。
在內存分配時,解決多線程同步的問題,有兩種方案:一種是對分配內從空間的動作做同步處理;另一種是把內存分配按照線程划分,即每個線程在Java堆中預先分配一小塊內存,成為本地線程分配緩沖。
內存分配完成后,虛擬機將分配到的內存空間都初始化為零,這一步操作保證了對象中成員對象的初始化。
對象在內存中的存儲結構
對象在內存中的存儲結構可以分為對象頭,實例數據,對齊填充。
對象頭
對象頭中存儲了兩部分信息,一部分存儲對象自身運行時數據,如HashCode,GC分代年齡,鎖狀態標志等。對象頭的另外一部分存儲着類型指針,指向對象類元數據,虛擬機通過這部分信息確定對象是哪一個類的實例。
如果對象為數組
,對象頭中還應該記錄數組的長度。因為不同對象可以通過類的元數據確定對象的大小,但是通過數組的元數據無法得知數組的長度。
實例數據
實例數據部分存儲着對象的有效信息,主要是成員變量。無論是從父類繼承的,還是在子類中定義的都要記錄下來。存儲的順序主要受虛擬機分配策略的影響。
HotSpot中的分配策略為:long/double,int,short/char,byte/boolean,opps(Ordinary Object Pointers)。相同寬度的字段總是被放到一起,在滿足這個條件的情況下,父類中定義的變量總是放到子類之前。
對齊填充
主要因為HotSpot自動內存管理系統要求對象起始地址必須是8字節的整數倍。