深入理解java虛擬機器之自動記憶體管理機制(三)
各類垃圾收集器與GC日誌
(一)垃圾收集器
一、Serial收集器
最基本、歷史最悠久的收集器。使用複製演算法,用在新生代,通常老年代用Serial old配合。GC過程需要stop the world。適用於client模式下的虛擬機器。
二、ParNew收集器
Serial多執行緒版本,採用複製演算法時,開啟多執行緒進行復制。能與Serial old配合,且是唯一能與cms收集器配合使用的新生代收集器。適用於server模式下的虛擬機器。在多cpu的環境
下效果更好。
三、Parallel Scavenge收集器
關注點是達到一個可控制的吞吐量,使用複製演算法。吞吐量=執行使用者程式碼時間/(執行使用者程式碼時間+垃圾收集時間)。
四、Serial Old收集器
Serial收集器的老年代版本,單執行緒收集器,使用標記-整理演算法,適用於Client模式下的虛擬機器。
五、Parallel Old收集器
Parallel Scavenge老年代版本,使用標記-整理演算法。
六、CMS收集器
以獲得最短回收停頓時間為目標的收集器。運作過程分為4個步驟。
1. 初始標記。該階段就是標記一下GC Roots能直接關聯的物件。
2. 併發標記。就是GC Roots tracing,該過程可與使用者執行緒併發執行。
3. 重新標記。修正併發標記期間發生變動的物件的標記。
4. 併發清除。該過程可與使用者執行緒併發執行。
缺點:
1. 對cpu資源敏感。如果不是多核,則由於存在兩個併發過程,會導致使用者程式執行緩慢。
2. 無法處理浮動垃圾,並且由於在清除階段與使用者執行緒併發執行,可能由於記憶體不足而導致一次full gc的產生。所謂浮動垃圾,就是在重新標記過後產生的垃圾。解決辦法是預留足夠記憶體。
3. 由於採用標記-清除演算法,所以容易產生大量記憶體碎片。
七、G1收集器
G1是革命性的。它有以下幾個特點。
1. 並行與併發。G1可以讓本來需要stw的動作通過併發的方式讓java程式繼續執行。
2. 分代收集。G1收集器自身就可以管理新生代和老年代的物件。
3. 空間整合。整體使用標記-整理演算法,區域性使用複製演算法。
4. 可預測的停頓。G1會建立可預測的停頓時間模型。
G1與傳統收集器不同的地方:
G1將java堆分為許多個大小相同的獨立區域(Region),新生代和老年代之間不再是物理隔離的,它們都是一部分Region的集合。
G1為什麼能建立可預測的停頓時間模型?
G1可以有計劃地避免在Java堆的進行全區域的垃圾收集,通過跟蹤各個Region獲得其收集價值大小(回收所獲得的空間大小以及回收所需要的時間的經驗值),在後臺維護一個優先列表;
每次根據允許的收集時間,優先回收價值最大的Region(名稱Garbage-First的由來),這就保證了在有限的時間內可以獲取儘可能高的收集效率。
G1如何解決一個物件被不同區域引用的問題?
不止G1要解決這個問題,其他收集器也要解決這個問題,其他收集器需要解決新生代與老年代之間的物件引用問題。都是通過使用Rememed Set來避免全堆掃描。G1的每個Region都有一
個與之對應的Remembered Set,虛擬機發現程式在對引用型別的資料進行讀寫操作時,會產生一個Write Barrier暫時中斷寫操作,檢查引用型別引用的物件是否處於不同的Region之中,如果是,就
通過CardTable把相關引用資訊記錄到被引用物件所屬的Region的Remembered Set之中。當進行記憶體回收時,在Gc根節點列舉範圍中加入Remembered Set即可保證不對全堆掃描也不會有遺漏。
關於OopMap 和 Remembered Set
總的來說:OopMap用於列舉根節點,Remembered Set 用來作可達性分析。
OopMap避免了全棧掃描,Remembered Set避免了全堆掃描。
新生代 GC(發生得非常頻繁)。一般來說, GC過程是這樣的:首先列舉根節點。根節點有可能在新生代中,也有可能在老年代中。這裡由於我們只想收集新生代(換句話說,不想收集老年代),
所以沒有必要對位於老年代的 GC Roots 做全面的可達性分析。但問題是,確實可能存在位於老年代的某個 GC Root,它引用了新生代的某個物件,這個物件你是不能清除的。那怎麼辦呢?
與OopMap一樣,用空間換時間,用一個數據結構儲存這種引用資訊,這樣在只需要再新生代上利用這兩個東西就能完成可達性的分析。RememberedSet記錄的是新生代的物件被老年代引用的關係。
所以“新生代的 GC Roots ” + “ RememberedSet 儲存的內容”,才是新生代收集時真正的 GC Roots 。
參考:https://dsxwjhf.iteye.com/blog/2201685
https://blog.csdn.net/ifleetingtime/article/details/78934379
G1的運作過程(不計維護Remembered Set)
1. 初始標記(Initial Marking)
僅標記一下GC Roots能直接關聯到的物件;
且修改TAMS(Next Top at Mark Start),讓下一階段併發執行時,使用者程式能在正確可用的Region中建立新物件;
需要"Stop The World",但速度很快;
2. 併發標記(Concurrent Marking)
進行GC Roots Tracing
耗時較長,但與使用者執行緒併發執行;
3. 最終標記(Final Marking)
為了修正併發標記期間變動的物件的標記記錄
上一階段物件的變化記錄線上程的Remembered Set Log;
這裡把Remembered Set Log合併到Remembered Set中;
需要"Stop The World",且停頓時間比初始標記稍長,但遠比並發標記短;
採用多執行緒並行執行來提升效率;
4. 篩選回收(Live Data Counting and Evacuation)
首先排序各個Region的回收價值和成本;
然後根據使用者期望的GC停頓時間來制定回收計劃;
最後按計劃回收一些價值高的Region中垃圾物件;
回收時採用"複製"演算法,從一個或多個Region複製存活物件到堆上的另一個空的Region,並且在此過程中壓縮和釋放記憶體;
(二)GC日誌
https://blog.csdn.net/xiaodu93/article/details/61926114