OopMap 和 RememberedSet

OopMap 用於枚舉 GC Roots,記錄棧中引用數據類型的位置。

記錄棧上本地變量到堆上對象的引用關係。其作用時,垃圾收集時,收集線程會堆棧上的內存進行掃描,看看哪些位置存儲了Reference類型。如果風險某個位置確實存的時Reference類型,就以爲着它鎖引用的對象這一次不能被回收。問題,棧上的本地變量表裏面只有一部分數據時Reference類型的,那些非Reference類型的數據對我們而言毫無用途,但我們還是不得不堆整個棧全部掃描一遍,這是堆時間和資源的一種浪費。

一個很自然的想法時,能不能用空間換時間?在某個時候把棧上代表的引用的位置全部記錄下來,這樣到真正gc的時候就可以直接讀取,而不用再一點一點的掃描了,Hotspot就是實現的。它使用一種叫做OopMak的數據結構來記錄這類信息。

一個線程以爲一個棧,一個棧由多個棧楨組成,一個棧楨對應一個方法,一個方法有多個安全點。GC發生時,程序首先運行到最近的一個安全點停下來,然後更新自己的OopMap,記錄棧上哪些位置代表着引用。枚舉根節點時,遞歸遍歷每個棧楨的OopMap ,通過棧中記錄的被引用的對象內存地址,即可找到這些對象(GC Roots)。

oopMap

  • 可以避免全棧掃描,加快枚舉根節點的速度

  • 可以幫助HotSpot實現準確式GC

RememberedSet 用於可達性分析

RememberedSet 用於處理這類問題:比如說,新生代 gc (它發生得非常頻繁)。一般來說, gc 過程是這樣的:首先枚舉根節點。根節點有可能在新生代中,也有可能在老年代中。這裏由於我們只想收集新生代(換句話說,不想收集老年代),所以沒有必要對位於老年代的 GC Roots 做全面的可達性分析。但問題是,確實可能存在位於老年代的某個 GC Root,它引用了新生代的某個對象,這個對象你是不能清除的。那怎麼辦呢?
      仍然是拿空間換時間的辦法。事實上,對於位於不同年代對象之間的引用關係,虛擬機會在程序運行過程中給記錄下來。對應上面所舉的例子,“老年代對象引用新生代對象”這種關係,會在引用關係發生時,在新生代邊上專門開闢一塊空間記錄下來,這就是 RememberedSet 。所以“新生代的 GC Roots ” + “ RememberedSet 存儲的內容”,纔是新生代收集時真正的 GC Roots 。然後就可以以此爲據,在新生代上做可達性分析,進行垃圾回收。

       我們知道, G1 收集器使用的是化整爲零的思想,把一塊大的內存劃分成很多個域( Region )。但問題是,難免有一個 Region 中的對象引用另一個 Region 中對象的情況。爲了達到可以以 Region 爲單位進行垃圾回收的目的, G1 收集器也使用了 RememberedSet 這種技術,在各個 Region 上記錄自家的對象被外面對象引用的情況。

參考:https://www.iteye.com/blog/dsxwjhf-2201685

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章