從彙編碼分析java物件的建立過程(推薦)
原始碼:
class T { int m = 8; } T t = new T();
彙編碼:
0 new #2 <T> 3 dup 4 invokespecial #3 <T.<init>> 7 astore_1 8 return
new #2
申請記憶體,在堆裡面建立一個新物件。
半初始化,新建物件中的m值是0。
dup
複製操作,因為invokespecial會消耗一份引用,所以先複製一份
invokespecial
4 invokespecial #3 <T.>
init是呼叫它的構造方法。
此時物件中的m值是8。
astore_1
將符號和物件建立關聯,即t和堆中的物件。
其實很簡單,今天看一個視訊說了半天。。。
知識點補充:java物件的建立過程
大家都知道,java使用new 關鍵字進行物件的建立,但這只是從語言層次上理解了物件的建立,下邊我們從jvm的角度來看看,物件是怎麼被創建出來的,即物件的建立過程。
物件的建立大概分為以下幾步:
1:檢查類是否已經被載入;
2:為物件分配記憶體空間;
3:為物件欄位設定零值;
4:設定物件頭;
5:執行構造方法。
第一步,當程式遇到new 關鍵字時,首先會去執行時常量池中查詢該引用所指向的類有沒有被虛擬機器載入,如果沒有被載入,那麼會進行類的載入過程,如果已經被載入,那麼進行下一步,為物件分配記憶體空間;
第二步,載入完類之後,需要在堆記憶體中為該物件分配一定的空間,該空間的大小在類載入完成時就已經確定下來了,這裡多說一點,為物件分配記憶體空間有兩種方式:
(1)第一種是jvm將堆區抽象為兩塊區域,一塊是已經被其他物件佔用的區域,另一塊是空白區域,中間通過一個指標進行標註,這時只需要將指標向空白區域移動相應大小空間,就完成了記憶體的分配,當然這種劃分的方式要求虛擬機器的對記憶體是地址連續的,且虛擬機器帶有記憶體壓縮機制,可以在記憶體分配完成時壓縮記憶體,形成連續地址空間,這種分配記憶體方式成為“指標碰撞”,但是很明顯,這種方式也存在一個比較嚴重的問題,那就是多執行緒建立物件時,會導致指標劃分不一致的問題,例如A執行緒剛剛將指標移動到新位置,但是B執行緒之前讀取到的是指標之前的位置,這樣劃分記憶體時就出現不一致的問題,解決這種問題,虛擬機器採用了迴圈CAS操作來保證記憶體的正確劃分;
(2)第二種也是為了解決第一種分配方式的不足而建立的方式,多執行緒分配記憶體時,虛擬機器為每個執行緒分配了不同的空間,這樣每個執行緒在分配記憶體時只是在自己的空間中操作,從而避免了上述問題,不需要同步。當然,當執行緒自己的空間用完了才需要需申請空間,這時候需要進行同步鎖定。為每個執行緒分配的空間稱為“本地執行緒分配緩衝(TLAB)”,是否啟用TLAB需要通過 -XX:+/-UseTLAB引數來設定。
第三步,分配完記憶體後,需要對物件的欄位進行零值初始化,物件頭除外,零值初始化意思就是對物件的欄位賦0值,或者null值,這也就解釋了為什麼這些欄位在不需要程序初始化時候就能直接使用;
第四步,這裡,虛擬機器需要對這個將要創建出來的物件,進行資訊標記,包括是否為新生代/老年代,物件的雜湊碼,元資料資訊,這些標記存放在物件頭資訊中,物件頭非常複雜,這裡不作解釋,可以另行百度;
第五步,也就是最後一步,執行物件的構造方法,這裡做的操作才是程式設計師真正想做的操作,例如初始化其他物件啊等等操作,至此,物件建立成功。
這裡其實講的比較粗淺,只是物件建立的大概過程,例如物件頭、類的載入等等都是非常複雜的過程,我會在接下來解釋。
總結
到此這篇關於從彙編碼分析java物件的建立過程的文章就介紹到這了,更多相關從彙編碼分析java物件的建立過程內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!