java對象創(chuàng)建過程解析

概述:

java作為一種高級語言,對開發(fā)者而言,創(chuàng)建一個對象是非常容易的,原因就是虛擬機底層做了很好的封裝,調(diào)用者不需要關(guān)注太多細節(jié)。通過new關(guān)鍵字,就可以創(chuàng)建一個對象。了解對象的創(chuàng)建過程,內(nèi)存布局對于性能上的一些優(yōu)化,理解很多原理是很有幫助的。

對象的創(chuàng)建:

對象的創(chuàng)建包含3個步驟:為對象分配內(nèi)存空間、初始化對象、將對象的內(nèi)存地址賦給引用。

分配內(nèi)存空間

創(chuàng)建對象的第一步就是要在內(nèi)存空間中劃分一塊內(nèi)存區(qū)域給對象使用,而對象所需要的內(nèi)存空間大小在類加載完成時便可以確認。虛擬機劃分內(nèi)存區(qū)域的方法主要有指針碰撞法、空閑列表法。

指針碰撞法

指針碰撞法主要適用于內(nèi)存絕對規(guī)整的情況,也就是將使用過的內(nèi)存與未使用的內(nèi)存嚴格分隔開。


image.png

如上圖所示,內(nèi)存區(qū)域是絕對規(guī)整的,中間的分界點指示器其實是一個內(nèi)存地址的指針,當有新的對象創(chuàng)建需要分配內(nèi)存空間時,只需要移動分界點指示器,也就是改變指針的值,使其往空閑區(qū)域移動就好。但是這種方式對于內(nèi)存不規(guī)整的情況就不適用,因為這種情況很容易造成內(nèi)存空間的浪費。

空閑列表

空閑列表的方式很容易理解,就是在虛擬機內(nèi)部維護一個空閑內(nèi)存區(qū)域的列表,記錄當前哪些內(nèi)存區(qū)域是可用的。當有對象創(chuàng)建需要分配內(nèi)存空間時,只要在列表中找到合適大小的區(qū)域,然后修改列表的內(nèi)容即可,這種方法不要求內(nèi)存區(qū)域的規(guī)整性??臻e列表法在對線程環(huán)境容易出現(xiàn)并發(fā)問題,若A線程申請了一塊內(nèi)存區(qū)域,但還沒來得及修改空閑列表,此時B線程申請內(nèi)存區(qū)域,有可能會分配已經(jīng)劃分給A線程的區(qū)域而出現(xiàn)失敗。因此虛擬機在底層采用CAS的方式來保證此操作的原子性和安全性。上篇介紹java運行時數(shù)據(jù)區(qū)域的文章中我們有提到在堆區(qū)可以為每個線程劃分一個TLAB(線程本地分配緩沖區(qū)),每個線程在自己的分配緩沖區(qū)為對象分配內(nèi)存,也可以解決多線程并發(fā)問題。

使用哪一種內(nèi)存分配方法取決于虛擬機的實現(xiàn),與虛擬機的GC算法相關(guān)聯(lián),上述兩種方法沒有孰優(yōu)孰劣,只有適不適合而已。

初始化對象

java程序員比較幸福的就是,虛擬機在底層默默地幫我們干了很多的活。當虛擬機為新創(chuàng)建的對象分配內(nèi)存區(qū)域后,會進行對象的初始化操作。如給對象中所有的基本數(shù)據(jù)變量賦上初始化值,以至于當我們未對它們進行賦值操作時就可以使用對象了。

內(nèi)存地址賦給引用

當內(nèi)存空間劃分成功,完成對象初始化操作后,虛擬機會將剛創(chuàng)建好對象的內(nèi)存地址賦給引用對象。完成此操作后,便可以在程序中通過引用訪問對象的實例數(shù)據(jù)。

對象的內(nèi)存布局:

在HotSpot虛擬機中,對象在內(nèi)存中存儲的布局可以劃分為3塊區(qū)域:對象頭、實例數(shù)據(jù)、對齊填充。

對象頭

對象頭中主要包含2部分的信息,一部分是用于存儲對象本身運行時的數(shù)據(jù),例如哈希嗎、分代年齡、鎖狀態(tài)標志、線程持有的鎖、偏向線程ID、偏向時間戳等;另一部分則是類型指針,即對象指向的是類信息,虛擬機可以根據(jù)此指針確定對象是屬于哪個類的實例。

實例數(shù)據(jù)

實例數(shù)據(jù)則是對象存儲的真正有效的信息,也是對象存儲的主要區(qū)域。對象中定義的所有字段的數(shù)據(jù)都會保存在這個區(qū)域,無論是定義在父類中還是子類中字段的數(shù)據(jù)都將存儲在該區(qū)域。

對齊填充

對齊填充并不是必須存在的,其本身沒有特殊的含義,僅僅是作為占位符存在而已。由于HotSpot要求對象起始地址必須是8字節(jié)的整數(shù)倍,也就是要求對象的大小是8字節(jié)的整數(shù)倍。因此在內(nèi)存分配時,若對象的大小不是8字節(jié)整數(shù)倍,對齊填充就會將其補全。

對象的訪問定位:

我們通常訪問一個對象是通過其引用來訪問,虛擬機的對象訪問定位主流的方式有兩種句柄法、直接指針。

句柄發(fā)

上圖展示的是使用句柄進行對象的訪問定位,由圖中可以看出,局部變量表中的對象引用指向的是句柄池,而句柄池中則保存著指向?qū)ο蟮恼嬲齼?nèi)存地址。直接指針更容易理解,就是沒有句柄池的存在,局部變量表中引用直接指向?qū)ο?,保存對象的?nèi)存地址。

兩者的區(qū)別很明顯,句柄法的優(yōu)勢在于reference中存儲的是句柄池的地址,這個地址是穩(wěn)定的,即使對象發(fā)生移動,內(nèi)存地址發(fā)生改變reference的內(nèi)容是不需要修改。而直接指針法主要體現(xiàn)在速度更快,因為其減少了一次指針定位的過程,開銷相對較小,缺點是若對象發(fā)生頻繁移動,reference中的值需要被頻繁修改。具體使用哪種方法要根據(jù)虛擬機實現(xiàn)的GC算法來決定,兩者各有優(yōu)勢。

結(jié)束語

終于分析完對象創(chuàng)建過程中的各種操作,文章偏理論化,沒有實際的案例講解。主要是希望通過分析其過程,來加深對底層實現(xiàn)的理解??梢詫懘a過程中理解,若不畏難者可以嘗試讀一下虛擬機的源碼。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容