java對象的創建
-
如何創建
正常情況下,我們新建對象是通過new來創建的,那麼new的背後又發生了什麼呢? -
類加載檢查
首先將去檢查這個指令的參數是否能在常量池中定位到一 個類的符號引用,並且檢查這個符號引用代表的類是否已被加載、解析和初始化過。如果沒 有,那必須先執行相應的類加載過程。
會出現類加載檢查情況:new關鍵詞
、對象克隆
、對象序列化
。 -
分配內存
當類加載檢查完成後,即需分配內存,對象所需內存的大小在類加載完成後便可完全確定,爲對象分配空間的任務等同於把 一塊確定大小的內存從Java堆中劃分出來
這個步驟有兩個問題:
-
如何劃分內存。
-
在併發情況下, 可能出現正在給對象A分配內存,指針還沒來得及修改,對象B又同時使用了原來的指針來 分配內存的情況。
劃分內存的方法:
-
“指針碰撞”(Bump the Pointer)
如果Java堆中內存是絕對規整的,所有用過的內 存都放在一邊,空閒的內存放在另一邊,中間放着一個指針作爲分界點的指示器,那所分配 內存就僅僅是把那個指針向空閒空間那邊挪動一段與對象大小相等的距離。
-
“空閒列表”(Free List)
如果Java堆中的內存並不是規整的,已使用的內存和空 閒的內存相互交錯,那就沒有辦法簡單地進行指針碰撞了,虛擬機就必須維護一個列表,記 錄上哪些內存塊是可用的,在分配的時候從列表中找到一塊足夠大的空間劃分給對象實例, 並更新列表上的記錄
-
解決併發問題的方法:
-
CAS(compare and swap)
虛擬機採用CAS配上失敗重試的方式保證更新操作的原子性來對分配內存空間的動作進行同步處理。
-
-
-
本地線程分配緩衝(Thread Local Allocation Buffer,TLAB)
把內存分 配的動作按照線程劃分在不同的空間之中進行,即每個線程在Java堆中預先分配一小塊內存。
-
初始化
把虛擬機需要將分配到的內存空間都初始化爲零值 各類型初值 -
設置對象頭
在HotSpot虛擬機中,對象在內存中存儲的佈局可以分爲3塊區域:對象頭(Header)、 實例數據(Instance Data)和對齊填充(Padding)。 HotSpot虛擬機的對象頭包括兩部分信息,第一部分用於存儲對象自身的運行時數據, 如哈希碼(HashCode)、GC分代年齡、鎖狀態標誌、線程持有的鎖、偏向線程ID、偏向時 間戳等。對象頭的另外一部分是類型指針,即對象指向它的類元數據的指針,虛擬機通過這個指 針來確定這個對象是哪個類的實例。初始化零值之後,虛擬機要對對象進行必要的設置,例如這個對象是哪個類的實例、如何才能找到類的元數據信息、對象的哈希碼、對象的GC分代年齡等信息。這些信息存放在對象的對象頭(Object Header之中。
-
執行 < init > 方法
執行方法,即對象按照程序員的意願進行初始化。對應到語言層面上講,就是爲屬性賦值(注意,這與上面的賦零值不同,這是由程序員賦的值),和執行構造方法。
init方法