溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何解密Java對象

發(fā)布時(shí)間:2021-10-23 16:00:46 來源:億速云 閱讀:126 作者:柒染 欄目:大數(shù)據(jù)

這篇文章將為大家詳細(xì)講解有關(guān)如何解密Java對象,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對相關(guān)知識有一定的了解。

在Java程序運(yùn)行過程中時(shí)時(shí)刻刻都有對象被創(chuàng)建出來,對象的創(chuàng)建方式有很多種,最常見的就是new,其次還有clone和反序列化。下面我們一起來解密對象的創(chuàng)建、內(nèi)存布局以及如何定位一個(gè)對象。

對象創(chuàng)建

當(dāng)我們在創(chuàng)建對象時(shí),首先會檢查創(chuàng)建對象的類能否在常量池中定位到符號引用,并檢查符號引用代表的類是否被加載、解析和初始化過,如果沒有則必須執(zhí)行相應(yīng)的類加載過程(這個(gè)后面也會單獨(dú)寫一篇文章講解)。

對象創(chuàng)建的過程大約有以下幾步:

  1. 虛擬機(jī)為對象分配內(nèi)存。對象所需內(nèi)存的大小在類加載完成以后便可完全確定。
  2. 設(shè)置對象必要信息。如對象是哪個(gè)類的實(shí)例、對象的hash碼、對象的GC分代年齡等信息
  3. 第三步執(zhí)行的時(shí)候,虛擬機(jī)其實(shí)認(rèn)為對象已經(jīng)創(chuàng)建成功,但是從Java程序的角度并沒有完成,下面會接著執(zhí)行方法,把對象按照編寫的代碼進(jìn)行初始化
 
分配對象內(nèi)存

為對象分配內(nèi)存本質(zhì)上就是從Java堆中劃分出一塊固定大小的內(nèi)存給Java對象使用。對象內(nèi)存分配主要有兩種:

  • 指針碰撞
  • 空閑列表

具體使用哪種方式取決于堆內(nèi)存是否工整,堆內(nèi)存是否工整本質(zhì)又取決于垃圾回收器是否帶有壓縮整理功能。

指針碰撞的分配方式用于在內(nèi)存工整的堆中進(jìn)行對象分配,所有被使用的內(nèi)存放在一邊,未被使用的在另一邊,中間放著一個(gè)指針作為分界點(diǎn)的指示器,當(dāng)為對象分配內(nèi)存時(shí),只需要將指針往未被使用的一邊挪動(dòng)與對象相等大小的距離就可以。

空閑列表適合在不規(guī)整的內(nèi)存中為對象分配內(nèi)存,虛擬機(jī)為了知道哪些內(nèi)存區(qū)域可用,必須維護(hù)一個(gè)列表,當(dāng)進(jìn)行內(nèi)存分配時(shí),則在列表中選取一個(gè)足夠大的空間劃分給對象使用。

對象分配在虛擬機(jī)中的分配并不是線程安全的,為了解決這個(gè)問題,主要有兩種解決方法:

  • CAS + 失敗重試
  • TLAB:每個(gè)線程預(yù)先在Java堆中預(yù)先分配一小塊內(nèi)存,稱為TLAB(本地線程分配緩沖),哪個(gè)線程需要分配內(nèi)存,就在哪個(gè)線程的TLAB上分配。只有TLAB用完了并且分配新的TLAB時(shí)需要同步鎖定。
 
對象內(nèi)存布局

對象在內(nèi)存中的布局主要有三塊:

  • 對象頭
  • 實(shí)例數(shù)據(jù)
  • 對齊填充
 
對象頭

對象頭主要用來存儲兩塊信息:

  • 存儲對象自身運(yùn)行的數(shù)據(jù)
  • 類型指針

對象自身運(yùn)行時(shí)的數(shù)據(jù)主要包括:哈希碼、GC分代年齡、鎖狀態(tài)標(biāo)志、線程持有的鎖、偏向線程ID、偏向時(shí)間戳等。這部分?jǐn)?shù)據(jù)的長度在32位和64位的虛擬機(jī)(未開啟壓縮指針)中分別為32位和64位。

存儲內(nèi)容標(biāo)志位狀態(tài)
對象哈希碼、對象分代年齡01未鎖定
指向鎖記錄的指針00輕量級鎖定
指向重量級鎖的指針10膨脹(重量級鎖定)
空,不需要記錄信息11GC標(biāo)記
偏向線程ID、偏向時(shí)間戳、對象分代年齡01可偏向

類型指針可以用來確定這個(gè)對象是哪個(gè)類的實(shí)例,但虛擬機(jī)的實(shí)現(xiàn)不是必須在對象上保留類型指針。

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

實(shí)例數(shù)據(jù)是對象真正存儲的有效信息,就是代碼中各種類型字段的內(nèi)容,無論是從父類還是子類中定義的,內(nèi)容存儲的順序會受到虛擬機(jī)分配策略參數(shù)和字段在Java源碼中定義順序的影響。 但是相同寬度的字段會分配到一起,在這個(gè)前提條件下,子類較窄的變量會插到父類變量的空隙之中。

 
對齊填充

對齊填充并不是必然存在,由于虛擬機(jī)的內(nèi)存管理要求對象其實(shí)地址必須是8字節(jié)的整數(shù)倍,也就是對象大小必須是8字節(jié)的整數(shù)倍,因此當(dāng)對象實(shí)例不是8字節(jié)的整數(shù)倍大小時(shí),需要通過對齊填充補(bǔ)全。

 
對象訪問

對象建立以后我們需要使用它,我們可以通過Java棧上的reference來操作堆上的具體對象,但是如何通過reference來找到具體的對象則是需要我們?nèi)ソ鉀Q的,目前主要有兩種方式:

  • 句柄
  • 直接指針

下圖是采用句柄的方式去訪問對象如何解密Java對象

下圖是采用直接指針的方式去訪問對象

如何解密Java對象  


通過上述兩種圖的對比,我們可以看出句柄的優(yōu)勢在于棧中的reference存儲的內(nèi)容是穩(wěn)定的句柄地址,不會因?yàn)閷ο蟮囊苿?dòng)而改變,但訪問會遜于直接指針,因?yàn)槎嗔艘淮沃羔樁ㄎ坏臅r(shí)間開銷。

直接指針的訪問方式的最大好處就是速度快,節(jié)省了一次指針定位的時(shí)間開銷。

關(guān)于如何解密Java對象就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI