G1中的技术细节
跨代引用
堆空間通常被劃分為新生代和老年代。由于新生代的垃圾收集通常很頻繁,如果老年代對象引用了新生代的對象,那么回收新生代的話,需要跟蹤從老 年代到新生代的所有引用,所以要避免每次 YGC 時掃描整個老年代,減少開銷。
RSet(記憶集)
記錄了其他 Region 中的對象到本 Region 的引用,RSet 的價值在于使得垃圾收集器不需要掃描整個堆,找到誰引用了當(dāng)前分區(qū)中的對象,只需要掃描 RSet 即可。RSet 本身就是一個 Hash 表,如果是在 G1 的話,則是在一個 Region 區(qū)里面。
CardTable(卡表)
由于做新生代 GC 時,需要掃描整個 OLD 區(qū),效率非常低,所以 JVM 設(shè)計了 CardTable,如果一個 OLD 區(qū) CardTable 中有對象指向 Y 區(qū), 就將它設(shè)為 Dirty (標(biāo)志位 1), 下次掃描時,只需要掃描 CARDTABLE 上是 Dirty 的內(nèi)存區(qū)域即可。字節(jié)數(shù)組 CARDTABLE 的每一個元素都對應(yīng)著其標(biāo)識的內(nèi)存區(qū)域中一塊特定大小的內(nèi)存塊,這個內(nèi)存塊被稱作“卡頁”(Card Page)。 一般來說,卡頁大小 都是以 2 的 N 次冪的字節(jié)數(shù),假設(shè)使用的卡頁是 2 的 10 次冪,即 1M,內(nèi)存區(qū)域的起始地址是 0x0000 的話,數(shù)組 CARD_TABLE 的第 0、1、2 號元素,分別 對應(yīng)了地址范圍為 0x0000~0x03FF、0x0400 ~ 0x07FF、0x0800~0x011FF 的卡頁內(nèi)存
(可以理解為記憶集是結(jié)構(gòu),卡表是實現(xiàn)類)
安全點
用戶線程暫停,GC 線程要開始工作,但是要確保用戶線程暫停的這行字節(jié)碼指令是不會導(dǎo)致引用關(guān)系的變化(比如剛好在new這個關(guān)鍵字,你暫停了,這個對象是有還是沒有,這個對象有沒有引用關(guān)聯(lián)著,引用有變化的都比較麻煩處理)。所以 JVM 會在字節(jié)碼指令中,選一些指令, 作為“安全點”,比如方法調(diào)用、循環(huán)跳轉(zhuǎn)、異常跳轉(zhuǎn)等,一般是這些指令才會產(chǎn)生安全點。
為什么它叫安全點,是這樣的,GC 時要暫停業(yè)務(wù)線程,并不是搶占式中斷(立馬把業(yè)務(wù)線程中斷)而是主動是中斷。
主動式中斷是設(shè)置一個標(biāo)志,這個標(biāo)志是中斷標(biāo)志,各業(yè)務(wù)線程在運行過程中會不停的主動去輪詢這個標(biāo)志,一旦發(fā)現(xiàn)中斷標(biāo)志為 True,就會在自己最近 的“安全點”上主動中斷掛起。
安全區(qū)域
要是業(yè)務(wù)線程都不執(zhí)行(業(yè)務(wù)線程處于 Sleep 或者是 Blocked 狀態(tài)),那么程序就沒辦法進(jìn)入安全點,對于這種情況,就必須引入安全區(qū)域。
安全區(qū)域是指能夠確保在某一段代碼片段之中, 引用關(guān)系不會發(fā)生變化,因此,在這個區(qū)域中任意地方開始垃圾收集都是安全的。我們也可以把安全區(qū) 城看作被擴(kuò)展拉伸了的安全點。
總結(jié)
- 上一篇: 关于中外利差的分析报告(转)
- 下一篇: 数据偏度介绍和处理方法