《深入理解Java虛擬機:JVM高級特性與最佳實踐》讀書筆記(二)

HotSpot虛擬機對象探秘

對象的創(chuàng)建

在類加載檢查通過后,接下來虛擬機將為新生對象分配內(nèi)存。對象所需內(nèi)存的大小在類加載完成后便可以完全確定。

假設(shè)當(dāng)Java堆中內(nèi)存時絕對規(guī)則的,即所有用過的內(nèi)存放在一邊,空閑的在另一邊,中間放著一個指針作為分界點的指示器。那么分配內(nèi)存就是把指針向空閑區(qū)域挪動一段與對象大小相等的距離,這種分配方式成為“指針碰撞”。

當(dāng)Java堆中的內(nèi)存并不是規(guī)則的,已使用的內(nèi)存和空閑的內(nèi)存相互交錯,虛擬機就必須維護一個列表,記錄上那些內(nèi)存塊是可用的。在分配時,從列表中找到一塊足夠大的空間劃分給對象實例,并更新表上的記錄,這種分配方式稱為“空閑列表”。

選擇哪種分配方式由Java堆是否規(guī)整決定,而Java堆是否規(guī)整又由所采用的垃圾收集器是否帶有壓縮整理功能決定。因此,在使用Serial、ParNew等帶Compact過程的收集器時,系統(tǒng)采用的分配算法是指針碰撞,而使用CMS這種基于Mark-Sweep算法的收集器時,通常采用空閑列表。

虛擬機如何解決在并發(fā)情況下頻繁創(chuàng)建對象帶來的線程不安全問題?

解決這個問題有兩種方案,一種是分配內(nèi)存空間的動作進行同步處理——實際上虛擬機采用CAS(注)配上失敗重試的方式保證更新操作的原子性;另一種是把內(nèi)存分配的動作按照線程劃分在不同的空間中進行,即每個線程在Java堆中預(yù)先分配一小塊內(nèi)存,稱為本地線程緩沖(TLAB)。哪個線程要分配內(nèi)存,就在哪個線程的TLAB上分配,只有TLAB用完并分配新的TLAB時,才需要同步鎖定。虛擬機是否使用TLAB,可以通過-XX:+/-UseTLAB參數(shù)來設(shè)定。

注:CAS,Compare and Swap即比較并替換,設(shè)計并發(fā)算法時常用到的一種技術(shù)。CAS有三個操作數(shù):內(nèi)存值V、舊的預(yù)期值A(chǔ)、要修改的值B,當(dāng)且僅當(dāng)預(yù)期值A(chǔ)和內(nèi)存值V相同時,將內(nèi)存值修改為B并返回true,否則什么都不做并返回false。

CAS的內(nèi)容和底層原理推薦博文:http://blog.csdn.net/hsuxu/article/details/9467651

和 http://www.itdecent.cn/p/fb6e91b013cc

內(nèi)存分配完畢后,虛擬機需要將分配到的內(nèi)存空間都初始化為零值(不包括對象頭),如果使用TLAB,這一工作過程也可以提前至TLAB分配時進行。這一步操作保證了對象的實例字段在Java代碼中可以不賦初始值就直接使用,程序能訪問到這些字段的數(shù)據(jù)類型所對應(yīng)的零值。

接下來,虛擬機要對對象進行必要的設(shè)置,例如這個對象是哪個類的實例、如何才能找到類的元數(shù)據(jù)信息、對象的哈希碼、對象的GC分代年齡等信息。這些信息存放在對象的對象頭(Object Header)之中。根據(jù)虛擬機當(dāng)前的運行狀態(tài)的不同,如是否啟用偏向鎖等,對象頭會有不同的設(shè)置方式。關(guān)于對象頭的具體內(nèi)容,稍后再做詳細介紹。

在上面工作都完成之后,從虛擬機的視角來看,一個新的對象已經(jīng)產(chǎn)生了,但從Java程序的視角來看,對象創(chuàng)建才剛剛開始——<init>方法還沒有執(zhí)行,所有的字段都還為零。所以,一般來說(由字節(jié)碼中是否跟隨invokespecial指令所決定),執(zhí)行new指令之后會接著執(zhí)行<init>方法,把對象按照程序員的意愿進行初始化,這樣一個真正可用的對象才算完全產(chǎn)生出來。

最后編輯于
?著作權(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)容