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