自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

保守式 GC 與準(zhǔn)確式 GC,如何在堆中找到某個(gè)對(duì)象的具體位置?

開(kāi)發(fā) 前端
對(duì)象的訪問(wèn)定位方式是由虛擬機(jī) GC 的具體實(shí)現(xiàn)來(lái)決定的,保守式 GC 使用的對(duì)象訪問(wèn)定位方式是使用句柄訪問(wèn),準(zhǔn)確式 GC 使用的對(duì)象訪問(wèn)定位方式是直接指針訪問(wèn)。

本文轉(zhuǎn)載自微信公眾號(hào)「飛天小牛肉」,作者小牛肉。轉(zhuǎn)載本文請(qǐng)聯(lián)系飛天小牛肉公眾號(hào)。

舉個(gè)例子:

User user = new User("Jack");

user 這個(gè)變量是存在棧中的對(duì)吧,name = Jack 的這個(gè) User 對(duì)象是存在堆中的,創(chuàng)建對(duì)象自然是為了后續(xù)使用該對(duì)象,那么如何在堆中找到這個(gè)對(duì)象的具體位置呢(也稱為對(duì)象的訪問(wèn)定位)?

對(duì)象的訪問(wèn)定位方式是由虛擬機(jī) GC 的具體實(shí)現(xiàn)來(lái)決定的,保守式 GC 使用的對(duì)象訪問(wèn)定位方式是使用句柄訪問(wèn),準(zhǔn)確式 GC 使用的對(duì)象訪問(wèn)定位方式是直接指針訪問(wèn)。

這里出現(xiàn)了幾個(gè)專有名詞哈,下面我來(lái)一一解釋 ??

老規(guī)矩,背誦版在文末。點(diǎn)擊閱讀原文可以直達(dá)我收錄整理的各大廠面試真題

保守式 GC 與使用句柄訪問(wèn)

談到垃圾回收必然離不開(kāi)對(duì)象標(biāo)記算法,眾所周知,目前主流的對(duì)象標(biāo)記算法就是可達(dá)性分析法,簡(jiǎn)單來(lái)說(shuō),可達(dá)性分析法是從 GC Roots 出發(fā)(注意是 GC Roots 說(shuō)明是有多個(gè) GC Root),當(dāng)某個(gè)對(duì)象到 GC Roots 沒(méi)有任何引用鏈時(shí),則該對(duì)象判定為可回收對(duì)象。

那么什么東西可以能作為 GC Roots 呢:

  • 在虛擬機(jī)棧中引用的對(duì)象,譬如各個(gè)線程被調(diào)用的方法堆棧中使用到的參數(shù)、局部變量、臨時(shí)變量等
  • 在本地方法棧中 JNI(即通常所說(shuō)的 Native 方法)引用的對(duì)象
  • 在方法區(qū)中類靜態(tài)屬性引用的對(duì)象,譬如 Java 類的引用類型靜態(tài)變量
  • 在方法區(qū)中常量引用的對(duì)象,譬如字符串常量池(String Table)里的引用
  • ......

針對(duì)到對(duì)象的訪問(wèn)定位(從棧中變量定位堆中對(duì)象)這個(gè)問(wèn)題,我們可以就取虛擬機(jī)棧(棧幀中的本地變量表)中引用的對(duì)象來(lái)說(shuō)明。

經(jīng)過(guò)上面的描述,問(wèn)題已經(jīng)簡(jiǎn)化成如何判斷虛擬機(jī)棧中的數(shù)據(jù)存的是一個(gè)引用還是一個(gè)基本數(shù)據(jù)?

打個(gè)比方:

從圖中可以看出,對(duì)于變量 a,JVM 在得到 a 的值后,肯定能夠立刻判斷出它不是一個(gè)引用,為什么?

因?yàn)橐檬且粋€(gè)地址,JVM 中地址是 32 位的,也就是 8 位的 16 進(jìn)制,很明顯 a 是一個(gè) 4 位 16 進(jìn)制,不能作為引用(這里的專業(yè)術(shù)語(yǔ)叫對(duì)齊檢查)。

同時(shí),JVM 對(duì)變量 d 也是能夠立刻判斷出它不是引用,因?yàn)?Java 堆的上下邊界是知道的,如圖中所標(biāo)識(shí)的堆起始地址和最后地址,JVM 發(fā)現(xiàn)變量 d 的值早就超出了 Java 堆的邊界,故認(rèn)為它不是引用(這里專業(yè)術(shù)語(yǔ)叫做上下邊界檢查)。

接下來(lái)才是重點(diǎn),對(duì)于變量 b(實(shí)際是一個(gè)引用) 和變量 c(實(shí)際就是一個(gè) int 型變量),發(fā)現(xiàn)他們兩個(gè)的值是一樣的,于是 JVM 就不能判斷了,在專業(yè)名稱上,基于這種方式的 GC 就稱為 “保守式 GC”,也稱為不能識(shí)別指針和非指針的 GC。

這里要說(shuō)明的是,雖然圖中畫(huà)了一個(gè)從變量 b 到對(duì)象 B 實(shí)例的一個(gè)箭頭,但 JVM 肯定是不知道的,畫(huà)個(gè)箭頭只是方便我們分析的

起始,這種保守式 GC 的內(nèi)存模型并不是上圖所示這般簡(jiǎn)單。

我們?cè)囅?,?dāng)執(zhí)行 b = null 之后,對(duì)象 B 的實(shí)例就應(yīng)該沒(méi)有任何指向了對(duì)吧,此時(shí)它就是個(gè)垃圾,應(yīng)該被回收掉。

但是 JVM 錯(cuò)誤的認(rèn)為變量 c 的值是一個(gè)引用,因?yàn)榇藭r(shí) JVM 很保守,擔(dān)心會(huì)判斷錯(cuò)誤,所以只好認(rèn)為 c 也是一個(gè)引用,這樣,JVM 認(rèn)為仍然有人在引用對(duì)象 B,所以不會(huì)回收對(duì)象 B。

這里似乎還看不出什么問(wèn)題,不過(guò)就是因?yàn)槟:臋z查,一些已經(jīng)死掉的對(duì)象被誤認(rèn)為仍有地方引用他們,GC 也就自然不會(huì)回收他們,從而引起了無(wú)用的內(nèi)存占用,造成資源浪費(fèi)。僅此而已。

更嚴(yán)重的問(wèn)題是,由于不知道疑似指針是否真的是指針,所以它們的值都不能改寫(xiě)。

比如上面保守式 GC 把 b 和 c 都看成是對(duì)象 B 實(shí)例的引用,一旦 B 這個(gè)對(duì)象實(shí)例移動(dòng)了,那么 b 和 c 的引用值都應(yīng)該修改,但如果 c 變量不是一個(gè)引用,而就單純只是一個(gè) int 型數(shù)據(jù)呢?

移動(dòng)對(duì)象就意味著要修正指針,換言之,對(duì)象就不可移動(dòng)了。這顯然是不可能的,GC 過(guò)程肯定伴隨存活對(duì)象的頻繁移動(dòng)。

有一種辦法可以在使用保守式 GC 的同時(shí)支持對(duì)象的移動(dòng),那就是增加一個(gè)間接層,不直接通過(guò)指針來(lái)實(shí)現(xiàn)引用,而是添加一層 “句柄”(handle)在中間,所有引用先指到一個(gè)句柄池里,再?gòu)木浔卣业綄?shí)際對(duì)象。這樣,要移動(dòng)對(duì)象的話,只要修改句柄池里的內(nèi)容即可。

于是保守式 GC 真正的內(nèi)存模型出來(lái)了:

通過(guò)上圖,不難發(fā)現(xiàn),在堆中增加了一個(gè)句柄池,當(dāng)對(duì)象 B 的實(shí)例更改存放地址后,JVM 只要改變句柄值,而不用改變變量 b 和變量 c 的值,這樣 JVM 就不用犯愁了,因?yàn)椴徽撟兞?c 是不是一個(gè)引用,之后用到 c 的地方,c 的值也沒(méi)有發(fā)生變化,可以正常使用。

不過(guò)很顯然,這樣的話引用的訪問(wèn)速度也就降低了。

簡(jiǎn)單總結(jié)下保守式 GC,也稱為不能識(shí)別指針和非指針的 GC,只能通過(guò)堆的上下邊界檢查和對(duì)齊檢查去判斷是否為一個(gè)引用。保守式 GC 有兩個(gè)缺點(diǎn):

  • 偽引用,如同上面所說(shuō)的,當(dāng) B = null 之后,本來(lái) B 對(duì)象應(yīng)該被當(dāng)作垃圾回收掉的,但是有變量 c 這么個(gè)偽引用存在,JVM 不敢動(dòng)手回收掉 B 對(duì)象
  • 為了支持對(duì)象的移動(dòng),增加了中間層句柄池,棧中的所有引用都指向這個(gè)句柄池中的地址,然后再?gòu)木浔刂姓业綄?shí)際對(duì)象,但是這樣占用了堆的空間并且降低了訪問(wèn)效率,需要兩次才能訪問(wèn)到真正的對(duì)象。

1996 年 1 月 23 日,Sun 發(fā)布 JDK 1.0,Java 語(yǔ)言首次擁有了商用的正式運(yùn)行環(huán)境,這個(gè) JDK 中所帶的虛擬機(jī)是 Classic VM,它采用的就是基于句柄的對(duì)象訪問(wèn)定位方式

準(zhǔn)確式 GC 與直接指針訪問(wèn)

與保守式 GC 相對(duì)的就是準(zhǔn)確式 GC,何為準(zhǔn)確式 GC?

就是我們準(zhǔn)確的知道,某個(gè)位置上面是否是指針,對(duì)于 Java 來(lái)說(shuō),就是知道內(nèi)存中某個(gè)位置的數(shù)據(jù)具體是什么類型,譬如內(nèi)存中有一個(gè) 32 bit 的整數(shù) 123456,虛擬機(jī)將有能力分辨出它到底是一個(gè)指向了 123456 的內(nèi)存地址的引用類型還是一個(gè)數(shù)值為 123456 的整數(shù),準(zhǔn)確分辨出哪些內(nèi)存是引用類型,這也是在垃圾收集時(shí)準(zhǔn)確判斷堆上的數(shù)據(jù)是否還可能被繼續(xù)使用的前提。

實(shí)現(xiàn)這種要求的方法有很多種,在 Java 中實(shí)現(xiàn)的方式是:從外部記錄下類型信息,存成映射表,在 HotSpot 中把這種映射表稱之為 OopMap,不同的虛擬機(jī)名稱可能不一樣,簡(jiǎn)而言之,OopMap 就是存著一系列信息的數(shù)據(jù)結(jié)構(gòu)。實(shí)現(xiàn)這種功能,需要虛擬機(jī)的解釋器和 JIT 編譯器支持,由他們來(lái)生成 OopMap。

現(xiàn)在主流的 Hotspot 虛擬機(jī),都拋棄掉了以前 Classic VM 基于句柄(Handle)的對(duì)象查找方式,采用基于直接指針訪問(wèn)的方式,這樣每次定位對(duì)象都少了一次間接查找的開(kāi)銷(xiāo),顯著提升執(zhí)行性能

最后放上這道題的背誦版:

?? 面試官:講一下對(duì)象的訪問(wèn)定位的方式

?? 小牛肉:對(duì)象的訪問(wèn)定位方式是由虛擬機(jī) GC 的具體實(shí)現(xiàn)來(lái)決定的,保守式 GC 使用的對(duì)象訪問(wèn)定位方式是使用句柄訪問(wèn),準(zhǔn)確式 GC 使用的對(duì)象訪問(wèn)定位方式是直接指針訪問(wèn):

所謂保守式 GC 就是虛擬機(jī)無(wú)法識(shí)別指針和非指針,這會(huì)導(dǎo)致兩個(gè)問(wèn)題,一個(gè)就是一些已經(jīng)死掉的對(duì)象無(wú)法被回收,占用內(nèi)存;第二個(gè)就是對(duì)象無(wú)法移動(dòng),為了解決這個(gè)問(wèn)題,在堆中引入了句柄池,所有引用先指到一個(gè)句柄池里,再?gòu)木浔卣业綄?shí)際對(duì)象。這樣,要移動(dòng)對(duì)象的話,只要修改句柄池里的內(nèi)容即可,虛擬機(jī)棧中存儲(chǔ)的就是對(duì)象的句柄地址。這就是使用句柄訪問(wèn),顯然它多了一次間接查找的開(kāi)銷(xiāo)

所謂準(zhǔn)確式 GC 就是虛擬機(jī)準(zhǔn)確的知道內(nèi)存中某個(gè)位置的數(shù)據(jù)具體是什么類型,具體的實(shí)現(xiàn)方式就是使用一個(gè)映射表 OopMap 記錄下類型信息,虛擬機(jī)棧中存儲(chǔ)的直接就是對(duì)象地址,這樣就不需要多一次間接訪問(wèn)的開(kāi)銷(xiāo)了,這就是直接指針訪問(wèn)


責(zé)任編輯:武曉燕 來(lái)源: 飛天小牛肉
相關(guān)推薦

2023-10-22 20:09:45

Linux命令

2018-05-28 11:10:08

Linux命令IP地址

2018-10-17 09:20:31

Linux命令重復(fù)文件

2024-05-23 12:40:06

2020-04-08 10:42:14

多云云計(jì)算云開(kāi)發(fā)

2012-08-15 14:44:53

GC

2022-08-08 06:48:56

JS語(yǔ)言對(duì)象

2012-07-16 13:26:53

2023-10-07 10:16:18

.Net8GC內(nèi)存

2009-08-10 19:30:09

運(yùn)維知識(shí)庫(kù)IT運(yùn)維管理廣通信達(dá)科技

2022-05-27 11:59:22

Linux內(nèi)存CPU

2019-11-27 14:41:50

Java技術(shù)語(yǔ)言

2022-12-12 11:14:06

LinuxID

2012-01-11 11:07:04

JavaJVM

2009-06-11 10:25:36

Java GC幽靈引用

2023-04-24 13:37:04

Unity游戲開(kāi)發(fā)

2014-05-08 14:13:00

Java面向GC

2021-01-21 08:00:25

JVM

2009-09-07 05:40:16

C#窗體位置C#窗體大小

2020-10-12 11:16:32

數(shù)組特定值元素
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)