JVM回收跨代垃圾的方式詳解
1. 跨代引用概述
在Java堆內(nèi)存中,年輕代和老年代之間存在的對象相互引用,假設(shè)現(xiàn)在要進(jìn)行一次新生代的YGC,但新生代中的對象可能被老年代所引用的,為了找到新生代中的存活對象,不得不遍歷整個老年代。這樣明顯效率很低下,那么如何快速識別并回收這種引用對象呢?
這就不得不提到Card Table(卡表)
和 Remember Set(記憶集,簡稱RSet)
了。
2. 跨代引用的處理方式
2.1 卡表(Card Table)
卡表是一種用于跟蹤年輕代對象被老年代對象引用的數(shù)據(jù)結(jié)構(gòu)。它將堆內(nèi)存劃分為一系列固定大小的區(qū)域(卡片),每個卡片記錄了年輕代對象被老年代對象引用的情況。在老年代垃圾回收時,垃圾收集器會掃描卡表,以確定哪些年輕代對象是存活的,即被老年代對象引用。
在 JVM 中,一個 card 的大小(通常是)512字節(jié)。在多線程并行收集時,每個線程可以批量掃描多個 card,一批 card 被稱為一個 stride。默認(rèn)一個 stride 含有 256個 card,即每個線程要每次掃描 512 * 256 = 128 K 的內(nèi)存區(qū)域。stride數(shù)量太多就會導(dǎo)致線程在stride之間切換的開銷增加,進(jìn)而導(dǎo)致 GC Pause 增長, strides 太少恐怕也會導(dǎo)致單次掃描的時間增長,進(jìn)而影響整個 GC Pause 。
2.2 記憶集(Remembered Sets)
伴隨 G1
垃圾收集器的誕生,傳統(tǒng)的老年代和新生代都從物理上的連續(xù)空間,變成了一個個物理上不連續(xù)的空間 region
。
JVM 針對這些Region 提供了一個數(shù)據(jù)結(jié)構(gòu),也就是 CSet(Collection Set)
,存儲任意年代的region
。
物理上不連續(xù)的 region
造成了新生代和老年的引用破碎化,新生代引用老年代,所以產(chǎn)生了 old->young
和young->old
的跨代對象引用,這時候 JVM 只要掃描 CSet
中的 R Set
即可。
邏輯上說每個Region都有一個RSet,RSet記錄了其他Region中的對象引用本Region中對象的關(guān)系。
每個Region會在自身的Remembered Set中紀(jì)錄下來自其他Region的指向自身的Card位置。這個Remembered Set
是一個Hash Table
,Key是別的Region的起始地址,Value是一個集合,里面的元素是Card Table
的Index。
RSet、Card和Region的關(guān)系
下圖表示了RSet、Card和Region的關(guān)系:
圖中是相互引用的三個region。R1 和 R3 的被細(xì)分到了card table 級別。R2 被 R1 和 R3的某些區(qū)域引用,所以 R2 的 RSet 會記錄到 R1 和 R2 的區(qū)域索引,即產(chǎn)生某些循環(huán)引用的作用。
一個 Region 的 RSet 如果有值,至少可以證明這個區(qū)域是有引用的;一個區(qū)域如果無值,則可以認(rèn)為這個區(qū)域不可達(dá),可以不掃描這個區(qū)域(Card Table 可以減少 Minor GC 掃描 old 區(qū)來理解 young 區(qū)的時間,RSet 則可以減少掃描生成 CSet 選取候選 region 的時間)。
在做YGC的時候,只需要選定young generation region的RSet作為根集,這些RSet記錄了old->young的跨代引用,避免了掃描整個old generation。而mixed gc的時候,old generation中記錄了old->old的 RSet,young->old的引用由掃描全部young generation region(的 card table)得到,這樣也不用掃描全部old generation region。所以RSet的引入大大減少了GC的工作量。
2.3 處理器屏障(Processor Barriers)
處理器屏障是一種硬件支持的機制,用于跟蹤對象之間的引用關(guān)系。當(dāng)發(fā)生引用修改時,處理器屏障可以監(jiān)測到對內(nèi)存的訪問,并通知垃圾收集器。垃圾收集器可以根據(jù)這些信息來更新引用關(guān)系,確保跨代引用被正確處理。
3. 總結(jié)
卡表只解決 youngGC 掃老年代的問題,而 RSet 則解決了(G1 對)所有 Region 的掃描問題??ū硗ㄟ^對外引用提示我們應(yīng)該掃描什么區(qū)域,這樣我們可以避開不用掃描的區(qū)域;RSet通過對內(nèi)引用提示我們應(yīng)該掃描什么區(qū)域,這樣我們可以避開不用掃描的區(qū)域。
跨代引用的垃圾回收是Java虛擬機中一個復(fù)雜而重要的問題。通過合理設(shè)計和優(yōu)化記憶集、卡表等數(shù)據(jù)結(jié)構(gòu),并結(jié)合并發(fā)標(biāo)記-清除算法、處理器屏障等技術(shù),可以有效地處理跨代引用,保證垃圾回收的效率和穩(wěn)定性,從而提高Java應(yīng)用程序的性能和可靠性。
以上就是JVM回收跨代垃圾的方式詳解的詳細(xì)內(nèi)容,更多關(guān)于JVM回收跨代垃圾的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringMVC請求、響應(yīng)和攔截器的使用實例詳解
攔截器(Interceptor) 它是一個Spring組件,并由Spring容器管理,并不依賴Tomcat等容器,是可以單獨使用的,這篇文章給大家介紹SpringMVC請求、響應(yīng)和攔截器的使用,感興趣的朋友一起看看吧2024-03-03IntelliJ?IDEA社區(qū)版2021.3配置SpringBoot項目詳細(xì)教程及失敗案例
IntelliJ?IDEA?2021.3.3是一款集成開發(fā)環(huán)境,用于Java和其他編程語言的開發(fā),下面這篇文章主要給大家介紹了關(guān)于IntelliJ?IDEA社區(qū)版2021.3配置SpringBoot項目詳細(xì)教程及失敗案例的相關(guān)資料,需要的朋友可以參考下2024-03-03Java編程之多線程死鎖與線程間通信簡單實現(xiàn)代碼
這篇文章主要介紹了Java編程之多線程死鎖與線程間通信簡單實現(xiàn)代碼,具有一定參考價值,需要的朋友可以了解下。2017-10-10RabbitMQ消費者限流實現(xiàn)消息處理優(yōu)化
這篇文章主要介紹了RabbitMQ消費者限流實現(xiàn)消息處理優(yōu)化,消費者限流是用于消費者每次獲取消息時限制條數(shù),注意前提是手動確認(rèn)模式,并且在手動確認(rèn)后才能獲取到消息,感興趣想要詳細(xì)了解可以參考下文2023-05-05Java CountDownLatch計數(shù)器與CyclicBarrier循環(huán)屏障
CountDownLatch是一種同步輔助,允許一個或多個線程等待其他線程中正在執(zhí)行的操作的ASET完成。它允許一組線程同時等待到達(dá)一個共同的障礙點2023-04-04配置JAVA環(huán)境變量中CLASSPATH變量的作用
這篇文章主要介紹了配置JAVA環(huán)境變量中CLASSPATH變量的作用,需要的朋友可以參考下2023-06-06SpringBoot整合Canal+RabbitMQ監(jiān)聽數(shù)據(jù)變更詳解
在現(xiàn)代分布式系統(tǒng)中,實時獲取數(shù)據(jù)庫的變更信息是一個常見的需求,本文將介紹SpringBoot如何通過整合Canal和RabbitMQ監(jiān)聽數(shù)據(jù)變更,需要的可以參考下2024-12-12一篇文章帶你理解Java Spring三級緩存和循環(huán)依賴
這篇文章主要介紹了淺談Spring 解決循環(huán)依賴必須要三級緩存嗎,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-09-09