概述:
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)存嚴格分隔開录语。
如上圖所示倍啥,內(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ī)整性〗ǔ担空閑列表法在對線程環(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ù)倍部翘,對齊填充就會將其補全。
對象的訪問定位:
我們通常訪問一個對象是通過其引用來訪問响委,虛擬機的對象訪問定位主流的方式有兩種句柄法新思、直接指針。
上圖展示的是使用句柄進行對象的訪問定位赘风,由圖中可以看出夹囚,局部變量表中的對象引用指向的是句柄池,而句柄池中則保存著指向?qū)ο蟮恼嬲齼?nèi)存地址邀窃。直接指針更容易理解荸哟,就是沒有句柄池的存在假哎,局部變量表中引用直接指向?qū)ο螅4鎸ο蟮膬?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)的理解∷椋可以寫代碼過程中理解百框,若不畏難者可以嘗試讀一下虛擬機的源碼闲礼。