Java垃圾回收(4)
G1:垃圾優先
G1收集器是熱點JVM中要實現的最新收集器。 自Java 7 Update 4以來,它一直是受支持的收集器。OracleGC團隊也公開表示,他們對低暫停GC的希望是完全實現的G1。 這篇文章來自我之前的垃圾收集博客文章:
問題:大堆意味著長暫停時間
并發標記和掃描(CMS)收集器是當前推薦的低暫停收集器,但是不幸的是,其暫停時間隨使用權區域中活動對象的數量而定。 這意味著,盡管使用較小的堆獲得較短的GC暫停相對容易,但是一旦開始使用10或100千兆字節的堆,時間就會開始增加。
CMS也不會“整理”其堆,因此在某個時間點您會遇到并發模式故障(CMF),從而觸發完整的gc。 一旦進入了完整的gc場景,您就可以預期每千兆字節活動對象大約1秒鐘的時間間隔。 使用CMS,您的100GB堆可能是等待1.5分鐘的GC暫停滴答定時炸彈……
良好的GC調優可以解決此問題,但有時只會將問題推向前進。 并發模式失敗和完整GC不可避免地會在足夠長的時間范圍內出現,除非您處在一小部分專門避免填充其使用期限的人員中。
G1堆布局
G1收集器試圖通過將堆分成不同的區域來將單個集合的暫停時間與堆的整體大小分開。 每個區域的大小都是固定的,介于1MB和32MB之間,JVM的目標是總共創建大約2000個區域。
您可能還記得以前的文章,其他收集器將堆分為Eden,Survior Space和Tenured內存池。 G1保留相同類別的池,但不是將它們作為連續的內存塊,而是將每個區域在邏輯上分類為這些池之一。
還有另一種類型的區域-大型區域。 這些對象用于存儲大小比大多數對象大的對象,例如,很長的數組。 任何大于區域大小50%的對象都存儲在巨大的區域中。 它們通過獲取連續位于內存中的多個普通區域并將它們視為單個邏輯區域來工作。
記憶集
當然,如果您將不得不掃描整個堆以找出哪些對象被標記為活動對象,那么將堆分成多個區域毫無意義。 實現此目標的第一步是將區域分成稱為卡的512字節段。 每張卡在卡標記表中都有一個1字節的條目。
每個區域都有一個關聯的記憶集或RSet-這是已寫入的卡集。 如果卡片中存儲的另一個區域中的某個對象指向該區域中的某個對象,則該卡片在記憶集中。
每當更改器寫入對象引用時,就會使用寫屏障來更新所記住的集合。 在幕后,記住的集合被分為不同的集合,以便不同的線程可以在沒有爭用的情況下運行,但是從概念上講,所有集合都是同一集合的一部分。
并發標記
為了標識哪些堆對象是活動的,G1執行活動對象的大部分并發標記。
- 標記階段標記階段的目標是弄清楚堆中哪些對象是活動的。 為了存儲哪些對象處于活動狀態,G1使用了標記位圖-堆中每64位存儲一個位。 從所有對象的根部開始跟蹤,并在標記位圖中使用活動對象標記區域。 這通常是并發的,但是有一個類似于CMS的“ 初始標記暫?!?,其中應用程序被暫停并且跟蹤根對象的第一級子級。 完成此步驟后,重新啟動線程。 G1需要保持對堆中生活內容的最新了解,因為不會在標記階段的同一暫停中清理堆。
- 標記階段標記階段的目標是使標記階段中有關活動對象的信息保持最新。 首先要做的是確定何時進行標記。 由一定百分比的堆已滿觸發。 這是通過從標記階段和此后的分配數量中獲取信息來計算的,并告訴G1其是否超過了要求的百分比。 G1使用前面提到的寫屏障來記錄對堆的更改,并將其存儲在一系列更改緩沖區中 。 更改緩沖區中的對象同時在標記位圖中標記。 當達到填充百分比時,將再次暫停更改程序線程并處理更改緩沖區,從而將更改緩沖區中的對象標記為活動狀態。
- 清理階段此時,G1知道哪些對象處于活動狀態。 由于G1專注于可用空間最大的區域,因此下一步是通過對活動對象進行計數來計算給定區域中的可用空間。 這是從標記位圖計算得出的,并且根據最有可能收集到哪些區域來對區域進行排序。 要收集的區域存儲在所謂的收集集或CSet中 。
疏散
與半球年輕一代在并行GC和CMS收集器中采用的方法類似,不會收集死物。 取而代之的是,將有生命的物體從某個區域撤離,然后將整個區域視為空閑區域。
G1對于如何回收活動物體很聰明–它不會嘗試在給定的周期內回收所有活動物體。 它針對的是可能會回收盡可能多空間的區域,僅將其撤離。 它通過計算活動對象在一個區域內的比例并選擇活動對象比例最低的區域來確定其目標區域。
將物體從多個其他區域撤離到自由區域。 這意味著G1在執行GC時會壓縮數據。 這由多個線程并行操作。 傳統的“平行GC”可以做到這一點,而CMS不這樣做。
與CMS和Parallel GC相似,存在終身制的概念。 也就是說,如果年輕對象在足夠的收藏中存活下來,它們就會變“舊”。 此數字稱為任職期限。 如果年輕的世代區域在任職期限內幸免,并保留了足夠的活物以避免被疏散,則該區域將得到提升。 首先是幸存者,最后是終身制地區。 它從未被疏散。
疏散失敗
不幸的是,G1仍然會遇到類似于并發模式故障的情況,在這種情況下,它會退回到Stop the World Full GC。 這稱為疏散失敗,在沒有空閑區域時發生。 沒有自由區域意味著沒有地方疏散物體。
理論上,與CMS中的并發模式故障相比,G1中發生疏散失敗的可能性較小。 這是因為G1會即時壓縮其區域,而不僅僅是等待壓縮失敗。
結論
盡管進行了壓縮和在低暫停時做出的努力,但G1并不能保證一定會獲勝,采用它的任何嘗試都應伴隨有客觀且可衡量的性能目標以及GC Log分析。 所需的方法超出了本博客文章的范圍,但希望我會在以后的文章中介紹它。
從算法上講,G1會遇到其他Hotspot收集器不會遇到的開銷。 值得注意的是,維護記憶集的成本。 并行GC仍然是推薦的吞吐量收集器,并且在許多情況下CMS都比G1更好。
現在說G1是否會比CMS收集器大勝還為時過早,但是在某些情況下,它已經為使用它的開發人員提供了好處。 隨著時間的流逝,我們將看到G1的性能限制是否真的是G1的限制,或者開發團隊是否僅需要更多的工程工作來解決那里的問題。
感謝John Oliver , Tim Monks和Martijn Verburg審閱了本期及以前的GC文章的草稿。
翻譯自: https://www.javacodegeeks.com/2013/07/garbage-collection-in-java-4.html
總結
以上是生活随笔為你收集整理的Java垃圾回收(4)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 资金流是什么意思 资金流介绍
- 下一篇: 一种编写测试的好方法