乘著年輕,多學習
接著上篇《JVM源碼分析之Java類載入過程》,本文將基於HotSpot實現對Java對象的創建過程進行深入分析。
定義兩個簡單的類AAA和BBB
通過「javap -c AAA「`查看編譯之後的位元組碼,具體如下:
Java中的new關鍵字對應jvm中的new指令,定義在InterpreterRuntime類中,實現如下:
new指令的實現過程:
1、其中pool是AAA的constant pool,此時AAA的class已經載入到虛擬機中,new指令後面的
#2
表示BBB類全限定名的符號引用在constant pool的位置;
2、方法
pool->klass_at
負責返回BBB對應的klassOop對象,實現如下:
如果常量池中指定位置(#2)的數據已經是個oop類型,說明BBB的class已經被載入並解析過,則直接通過
(klassOop)entry.get_oop
返回klassOop;否則表示第一次使用BBB,需要解析BBB的符號引用,並載入BBB的class類,生成對應的instanceKlass對象,並更新constant pool中對應位置的符號引用;
3、
klass->check_valid_for_instantiation
可以防止抽象類被實例化;
4、
klass->initialize
實現如下:
如果BBB的instanceKlass對象已經初始化完成,則直接返回;否則通過
initialize_impl
方法進行初始化,整個初始化演算法分成11步,具體實現如下:
step1
通過ObjectLocker在初始化之前進行加鎖,防止多個線程併發初始化。
step2
如果當前instanceKlass處於being_initialized狀態,且正在被其它線程初始化,則執行
ol.waitUninterruptibly
等待其他線程完成後通知。
step3
如果當前instanceKlass處於being_initialized狀態,且被當前線程初始化,則直接返回。
其實對於這個step的處理我有疑問,什麼情況會走到這一步?經過RednaxelaFX大大提點,如下情況會執行step3:
例如A類有靜態變數指向一個new B類實例,B類里又有靜態變數指向new A類實例,這樣外部用A時要初始化A類,初始化過程中又要觸發B類初始化,B類初始化又再次觸發A類初始化。
step4
如果當前instanceKlass處於fully_initialized狀態,說明已經初始化完成,則直接返回;
step5
如果當前instanceKlass處於initialization_error狀態,說明初始化失敗了,拋出異常。
step6
設置當前instanceKlass的狀態為 being_initialized;設置初始化線程為當前線程。
如果當前instanceKlass不是介面類型,並且父類不為空,且還未初始化,則執行父類的初始化。
step8
通過方法執行靜態塊代碼,實現如下:
this_oop->class_initializer
JavaCalls::call
執行代碼塊邏輯,再下一層就是具體操作系統的實現了。
step9
如果初始化過程沒有異常,說明instanceKlass對象已經初始完成,則設置當前instanceKlass的狀態為 fully_initialized,最後通知其它線程初始化已經完成;否則執行step10 and 11。
step10 and 11
如果初始化發生異常,則設置當前instanceKlass的狀態為 initialization_error,並通知其它線程初始化發生異常。
5、如果instanceKlass初始化完成,
klass->allocate_instance
會在堆內存創建instanceOopDesc對象,即類的實例化;
instanceOopDesc
當在Java中new一個對象時,本質是在堆內存創建一個instanceOopDesc對象。
instanceOopDesc在實現上繼承自oopDesc,其中oopDesc定義如下:
當然,這只是 oopDesc的部分實現,oopDesc包含兩個數據成員:_mark 和 _metadata。
1、_mark是markOop類型對象,用於存儲對象自身的運行時數據,如哈希碼(HashCode)、GC分代年齡、鎖狀態標誌、線程持有的鎖、偏向線程ID、偏向時間戳等等,佔用內存大小與虛擬機位長一致,更具體的實現可以閱讀 《java對象頭的HotSpot實現分析》
2、_metadata是一個聯合體,其中wideKlassOop和narrowOop都是指向InstanceKlass對象的指針,wide版是普通指針,narrow版是壓縮類指針(compressed Class pointer)
instanceOopDesc對象的創建過程
instanceOopDesc對象通過
instanceKlass::allocate_instance
進行創建,實現過程如下:
1、
has_finalizer
判斷當前類是否包含不為空的finalize方法;
2、
size_helper
確定創建當前對象需要分配多大內存;
3、
CollectedHeap::obj_allocate
從堆中申請指定大小的內存,並創建instanceOopDesc對象,實現如下:
4、如果當前類重寫了finalize方法,且非空,需要把生成的對象封裝成Finalizer對象並添加到 Finalizer鏈表中,對象被GC時,如果是Finalizer對象,會將對象賦值到pending對象。Reference Handler線程會將pending對象push到queue中,Finalizer線程poll到對象,先刪除掉Finalizer鏈表中對應的對象,然後再執行對象的finalize方法;