前言
Java的中的以下幾種對象,在內存中存放位置:
- new出來的普通對象
- 克隆出來的對象
- 反序列化出來的對象
- Class對象
- 數組
前三種是存放在Java堆中的,後兩種是存放在方法區中的。
以下重點描述的是Java堆中創建一個對象的流程。
第一步:檢查類是否加載過
第一步:檢查對象所屬的類是否才運行時常量池中定位到了一個類的符號引用,並且這個類是否已被加載,解析和初始化過,如果沒有,需要先進行加載,解析和初始化。
第二步:給對象分配內存空間
第二步:爲我們的新生對象分配內存,對象所需內存大小在類加載完成後便可完全確定。有以下兩種分配方法:
- 指針碰撞:Java堆中的內存是規整的,一邊是使用完的內存,一邊是沒使用的內存,臨界處用一個指針來標記,只要將指針向空閒的那邊移動對象所需的內存大小即可完成對象內存的分配。
- 空閒列表:Java堆中的內存不是規整的,虛擬機就必須維護一個列表,記錄哪些內存是可用的,在給對象分配內存的時候,找到一塊足夠的內存分給對象,並更新這個列表即可,這個列表就是空閒列表。
Java堆中的內存是否規整,是由垃圾收集器是否帶有壓縮整理功能決定的。
serial、ParNew等帶有Compact過程的收集器,系統採用的分配算法是指針碰撞。
而採用CMS這種基於Mark-Sweep算法的收集器,系統採用的是空閒列表分配內存空間的方法。
在創建對象的過程中,爲了防止併發創建對象的過程中對內存修改可能導致的線程安全問題有兩種解決方式:
- 虛擬機採用CAS配上失敗重試的方式保證更新操作的原子性。
- 把內存分配的操作按照線程劃分在不同的空間之中進行,即每個線程在Java堆中預先分配一小塊內存,稱爲本地線程分配緩衝TLAB,哪個線程要分配內存,就在那個線程的TLAB上分配,只有TLAB用完並分配新的TLAB時,才需要同步鎖定,虛擬機是否採用TLAB,可以通過-XX:+/-UseTLAB參數來設定。
第三步:給分配的空間初始化零值
內存分配完成後,虛擬機需要將分配到的內存空間都初始化零值,其中不包括對象頭。如果採用TLAB,這一工作可能提前至TLAB分配時進行。
對象頭中,存有:
- 這個對象是哪個類的實例
- 如何能找到類的元數據信息
- 對象的哈希碼
- 對象的GC的分代命令
- 是否採用偏向鎖等
第四步:執行對象的init方法初始化
把對象按照程序員的意願初始化後,這樣一個真正可用的對象纔算完全產生出來。