java對象的創建過程


  java是面向對象的編程語言,那么對象的創建過程是怎樣的呢?(本文只討論普通的對象,不包括數組和Class對象)。

1.類加載檢查

  虛擬機遇到一條new指令時,首先將去檢查這個指令的參數是否能在常量池中定位到一 個類的符號引用,並且檢查這個符號引用代表的類是否已被加載、解析和初始化過。如果沒 有,那必須先執行相應的類加載過程。

  new指令對應到語言層面上講是,new關鍵詞、對象克隆、對象序列化。

2.分配內存

  在類加載檢查通過后,接下來虛擬機將為新生對象分配內存。對象所需內存的大小在類 加載完成后便可完全確定,為對象分配空間的任務等同於把 一塊確定大小的內存從Java堆中划分出來。

這個步驟有兩個問題:

1.如何划分內存。

2.在並發情況下, 可能出現正在給對象A分配內存,指針還沒來得及修改,對象B又同時使用了原來的指針來 分配內存的情況。

划分內存的方法:

  • “指針碰撞”(Bump the Pointer)

如果Java堆中內存是絕對規整的,所有用過的內 存都放在一邊,空閑的內存放在另一邊,中間放着一個指針作為分界點的指示器,那所分配 內存就僅僅是把那個指針向空閑空間那邊挪動一段與對象大小相等的距離。

  • “空閑列表”(Free List)

如果Java堆中的內存並不是規整的,已使用的內存和空 閑的內存相互交錯,那就沒有辦法簡單地進行指針碰撞了,虛擬機就必須維護一個列表,記 錄上哪些內存塊是可用的,在分配的時候從列表中找到一塊足夠大的空間划分給對象實例, 並更新列表上的記錄

解決並發問題的方法:

  • CAS(compare and swap)

虛擬機采用CAS配上失敗重試的方式保證更新操作的原子性來對分配內存空間的動作進行同步處理。

  • 本地線程分配緩沖(Thread Local Allocation Buffer,TLAB)

把內存分 配的動作按照線程划分在不同的空間之中進行,即每個線程在Java堆中預先分配一小塊內存。

3.初始化

  內存分配完成后,虛擬機需要將分配到的內存空間都初始化為零值(不包括對象頭), 如果使用TLAB,這一工作過程也可以提前至TLAB分配時進行。這一步操作保證了對象的實例字段在Java代碼中可以不賦初始值就直接使用,程序能訪問到這些字段的數據類型所對應的零值。

4.設置對象頭

  在HotSpot虛擬機中,對象在內存中存儲的布局可以分為3塊區域:對象頭(Header)、 實例數據(Instance Data)和對齊填充(Padding)。 HotSpot虛擬機的對象頭包括兩部分信息,第一部分用於存儲對象自身的運行時數據, 如哈希碼(HashCode)、GC分代年齡、鎖狀態標志、線程持有的鎖、偏向線程ID、偏向時 間戳等。對象頭的另外一部分是類型指針,即對象指向它的類元數據的指針,虛擬機通過這個指 針來確定這個對象是哪個類的實例。

  初始化零值之后,虛擬機要對對象進行必要的設置,例如這個對象是哪個類的實例、如何才能找到類的元數據信息、對象的哈希碼、對象的GC分代年齡等信息。這些信息存放在對象的對象頭(Object Header之中。

5.執行<init>方法

  執行<init>方法,即對象按照程序員的意願進行初始化。對應到語言層面上講,就是為屬性賦值(注意,這與上面的賦零值不同,這是由程序員賦的值),和執行構造方法。

最后上一張圖:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM