对象创建的过程细节是怎样的?一起来探讨内存变化细节
目錄
一、對象的實例化
對象創建的幾種方式
對象創建的步驟
二、對象的內存布局
三、對象的訪問定位
JVM是如何通過棧幀中的對象引用訪問到其內部的對象實例呢?
1.句柄訪問
2.直接指針(HotSpot采用這種方式)
一、對象的實例化
對象創建的幾種方式
1.new關鍵字。
? ? (1)直接new?Xxx()
? ? (2)調用Xxx的靜態方法,返回一個新建的對象。
? ? (3)XxxBuilder/XxxFactory使用工廠模式獲取對象。
2.Class的newInstance():反射的方式,只能調用空參構造器,權限必須是public,jdk9標記為該方法為已過時。
3.Constructor的newInstance(Xxx):反射的方式,可以調用空參、帶參的構造器,權限沒有要求。
4.使用clone():不調用任何構造器,當前類需要實現Cloneable接口,實現clone()。
5.使用反序列化:從文件中、從網絡中獲取一個對象的二進制流。
6.第三方庫Objenesis。
對象創建的步驟
①.從字節碼角度看:
反編譯java -p -v xxx.class
②.從執行步驟看:
1.判斷對象對應的類是否加載、鏈接、初始化。
? ? 虛擬機遇到一條new指令,首先去檢查這個指令的參數能否在Metaspace的常量池中定位到一個類的符號引用,并且檢查這個符號引用代表的類是否已經被加載、解析和初始化。(即判斷類元信息是否存在)。如果沒有,那么在雙親委派模式下,使用當前類加載器以ClassLoader+包名+類名為Key進行查找對應的.class文件。如果沒有找到文件,則拋出ClassNotFountException異常,如果找到,則進行類加載,并生成對應的Class類對象。
2.為對象分配內存。
? ? (1)首先計算對象占用空間大小,接著在堆中劃分一塊內存給新對象。
? ? ?? ?如果實例成員變量是引用變量,僅分配引用變量空間即可,即4個字節大小。
? ? (2)分配堆空間,如果內存規整——指針碰撞。
? ? ?? ?如果內存是規整的,那么虛擬機將采用的是指針碰撞法(Bump?The?Pointer)來為對象分配內存。
?? ?? ? 意思是所有用過的內存在一邊,空閑的內存在另一邊,中間放著一個指針作為分界點的指示器,分配內存就僅僅是把指針指向空閑那邊挪動一段與對象大小相等的距離罷了。如果垃圾收集器選擇的是Serial、ParNew這種基于壓縮算法的(內存會進行整理,內存比較規整),虛擬機采用這種分配方式。一般使用帶有compact(整理)過程的收集器時,使用指針碰撞。
? ? (3)分配堆空間,如果內存不規整——虛擬機需要維護一個列表,空閑列表分配.
?? ?? ? 如果內存不是規整的,已使用的內存和未使用的內存相互交錯,那么虛擬機將采用的是空閑列表法來為對象分配內存。
?? ?? ? 意思是虛擬機維護了一個列表,記錄上那些內存塊是可用的,再分配的時候從列表中找到一塊足夠大的空間劃分給對象實例,并更新列表上的內容。這種分配方式成為“空閑列表(Free?List)”。
? ? (4)說明
?? ?? ? 選擇哪種分配方式由Java堆是否規整決定,而Java堆是否規整又由所采用的垃圾收集器是否帶有壓縮整理功能決定。
3.處理并發安全問題
? ? (1)采用CAS失敗重試、區域加鎖保證更新的原子性
? ? (2)每個線程預先分配一塊TLAB——通過-XX:+/-UseTLAB參數來設定是否開啟TLAB
4.初始化分配到的空間——所有屬性設置默認值,保證對象實例字段在不賦值時可以直接使用
5.設置對象的對象頭
? ? 將對象的所屬類(即類的元數據信息)、對象的HashCode和對象的GC信息、鎖信息等數據存儲在對象的對象頭中。這個過程的具體設置方式取決于JVM實現。
6.執行init方法進行初始化(調用代碼塊、構造器進行初始化)
? ? 在Java程序的視角來看,初始化才正式開始。初始化成員變量,執行實例化代碼塊,調用類的構造方法,并把堆內存對象的首地址賦值給引用變量。
? ? 因此一般來說(由字節碼中是否根鎖有invokespecial指令所決定),new指令之后會接著就是執行方法,把對象按照程序員的意愿進行初始化,這樣一個真正可用的對象才算完全創建出來。
二、對象的內存布局
1.對象頭。包含兩部分:
?? ?(1)運行時元數據(Mark Word)
?? ?? ? 哈希值(HashCode)
?? ?? ? GC分代年齡
?? ?? ? 鎖狀態標志
?? ?? ? 線程持有的鎖
?? ?? ? 偏向線程ID?? ?
?? ?? ? 偏向時間戳
? ? (2)類型指針——指向類元數據的InstanceKlass,確定該對象所屬的類型。
說明:如果是數組,還要記錄數組的長度。
2.實例數據(Instance?Data)
? ? 說明:它是對象真正存儲的有效信息,包括程序代碼中定義的各種類型的字段(包括從父類繼承下來的和本身擁有的字段)
? ? 規則:(1)相同寬度的字段總是被分配在一起。(2)父類中定義的變量會出現在子類之前。(3)如果CompactFields參數為true(默認為true),子類的窄變量可能插入到父類變量的空隙。
3.對其填充(Padding)——不是必須的,也沒特別含義,僅僅起到占位符的作用。
4.小結:圖示舉例
三、對象的訪問定位
JVM是如何通過棧幀中的對象引用訪問到其內部的對象實例呢?
答:定位,通過棧上reference訪問(有兩種方式)。
1.句柄訪問
優點:reference中存儲穩定句柄地址,對象被移動(垃圾收集時移動對象很普遍)時只會改變句柄中實例數據指針即可,reference本身不需要被修改。
缺點:還要單獨開辟一塊句柄池,作為“中間商”
2.直接指針(HotSpot采用這種方式)
優缺點與句柄訪問相反
總結
以上是生活随笔為你收集整理的对象创建的过程细节是怎样的?一起来探讨内存变化细节的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用final修饰的HashMap,到底可
- 下一篇: jvm执行引擎全解,java解释器即时编