在 HotSpot 虛擬機中,對象在內存中存儲的佈局分爲 3 塊區域:算法
對象頭記錄了對象在運行過程當中所須要使用的一些數據:數組
對象頭可能包含類型指針,經過該指針能肯定對象屬於哪一個類。若是對象是一個數組,那麼對象頭還包括數組長度。函數
實例數據部分就是成員變量的值,其中包括父類成員變量和本類成員變量。佈局
用於確保對象的總長度爲 8 字節的整數倍。性能
HotSpot 要求對象的總長度爲 8 字節的整數倍。因爲對象頭必定是 8 字節的整數倍,但實例數據部分的長度是任意的,所以須要對齊補充字段確保整個對象的總長度爲 8 的整數倍。spa
對齊補充並非必然存在,也沒有特別的含義,它僅僅起着佔位符的做用。
虛擬機遇到一條 new 指令時,首先檢查常量池中是否有這個類的符號引用,而且檢查這個符號引用所表明的類是否已被加載、解析和初始化過。若是沒有,那麼必須先執行相應的類加載過程。線程
對象所需內存的大小在類加載完成後即可徹底肯定,接下來從堆中劃分一塊對應大小的內存空間給新的對象。分配堆中內存有兩種方式:指針
若是 JVM 的垃圾收集器採用複製算法或標記-整理法,那麼堆中空閒內存是完整的區域,而且空間內存和已使用內存之間由一個指針標記。爲對象分配內存時,只需移動指針便可。這種在完整空閒區域上經過移動指針來分配內存的方式稱爲「指針碰撞」。對象
若是 JVM 的垃圾收集器採用標記-清除算法,那麼堆中空閒區域和已使用區域交錯,所以須要用一張「空閒列表」來記錄堆中哪些區域是空閒區域,從而在建立對象的時候根據這張「空閒列表」找到空閒區域,並分配內存。內存
分配完內存後,爲對象中的成員變量賦上初始值,設置對象頭信息,調用對象的構造函數方法進行初始化。
至此,整個對象的建立過程就完成了。
全部對象的存儲空間都是在堆中分配的,可是這個對象的引用倒是在堆棧中分配的。也就是說在創建一個對象時兩個地方都分配內存,在堆中分配的內存實際創建這個對象,而在堆棧中分配的內存只是一個指向這個堆對象的指針(引用)而已。
那麼根據引用存放的地址類型的不一樣,對象有不一樣的訪問方式。
堆中須要有一塊叫作「句柄池」的內存空間,句柄中包含了對象實例數據與類型數據各自的具體地址信息。
引用類型的變量存放的是該對象的句柄地址(reference)。訪問對象時,首先須要經過引用類型的變量找到該對象的句柄,而後根據句柄中對象的地址找到對象。
引用類型的變量直接存放對象的地址,從而不須要句柄池,經過引用可以直接訪問對象。但對象所在的內存空間須要額外的策略存儲對象所屬的類信息的地址。
HotSpot 採用直接指針方式訪問對象,由於它只須要一次尋址操做,從而性能比句柄訪問方式快一倍。但它須要額外的策略存儲對象在方法區中類信息的地址。