jvm調優之垃圾回收器

        不同虛擬機一般都會提供不同的參數供用戶根據自己的應用特點和要求組合出各個內存分代所使用的垃圾收集器。

        圖中展示了7種不同分代的垃圾收集器,如果兩個收集器之間存在連線,就說明他們可以搭配使用,他們所處的區域,他們屬於那個區域的垃圾收集器。

並行和併發垃圾收集器

    並行:描述的是多條垃圾收集線程之間的關係,同一時間多條垃圾收集線程協同工作,通常用戶線程是等待狀態。

    併發:描述的是垃圾收集線程和用戶線程之間的關係,同一時間垃圾收集線程和用戶線程同時在工作,只是此時用戶線程吞吐量受到垃圾收集線程影響。

1.Serial收集器
        新生代的單線程收集器。

從圖中可以看出,他執行垃圾回收時需要暫停所有用戶線程,單線程執行垃圾回收。對於新生代收集幾十兆或者一兩百兆,停頓時間在十幾、幾十毫秒、一百毫秒以內的不頻繁發生垃圾收集的,Serial收集器是可以接受的,一般用於客戶端模式下的虛擬機。

2.ParNew收集器
    ParNew收集器本質上是Serial收集器的多線程並行版本,除了多線程執行垃圾收集之外,兩個收集器的控制參數(-XX:+SurvivorRatio、-XX:+PretenureSizeThreshold、-XX:+HandlePromotionFailure)、收集算法、Stop The World、對象分配規則、回收策略等都一樣。ParNew收集器是激活CMS收集器後的默認新生代收集器;ParNew收集器和CMS收集器配合是JDK9之後不再是官方推薦收集器組合。默認開啓的收集線程數和處理器核心數量相同,也可以使用-XX:+ParallelGCThreads參數老控制垃圾收集的線程數。

3.Parallel Scavenge收集器
        Parallel Scavenge收集器新生代收集器,基於標記複製算法實現的並行多線程收集器。是關注吞吐量的收集器,因此也被愛叫做吞吐量優先收集器。所謂吞吐量就是處理器運行用戶代碼的時間與處理器總消耗時間的比值,即:吞吐量=運行用戶代碼時間/(運行用戶代碼時間+運行垃圾收集時間)高吞吐量能以最高效率利用處理器資源,儘快完成任務,適用於後臺運算不太需要用戶太多交互的分析任務。
        控制吞吐量的兩個參數:最大垃圾停頓時間(-XX:+MaxGCPauseMillis)和直接設置吞吐量大小(-XX:+GCTimeRatio);同時還有一個自適應調節參數:-XX:+UseAdaptiveSizePolicy。因此只需要設置好內存大小(-Xmx最大堆)然後使用-XX:+MaxGCPauseMillis設置最大停頓時間和-XX:+GCTimeRatio設置吞吐率,給虛擬機一個優化目標,具體調節參數工作就有虛擬機完成。

4.Serial Old收集器
        
Serial收集器的老年代版本。

5.Parallel Old收集器
       
Parallel Scavenge收集器的老年代版本,支持併發收集,基於標記整理算法。

        在關注吞吐量和處理器資源較爲稀缺的場合,可以優先考慮Parallel Scavenge和Parallel Old收集器組合。

6.CMS收集器
        CMS(Concurrent Mark Sweep)收集器從名字可以看出是基於標記清除算法的收集器,一種以獲取最短回收停頓時間爲目標的收集器,因此一些文章稱爲併發低停頓收集器,CMS適合應用集中在互聯網網站或者基於瀏覽器的B/S系統的服務端上,停頓時間儘可能短,以提供良好的用戶交互體驗。

運行過程分爲四個步驟:

​​​​​        1)初始標記 需要STW 僅從GC Roots開始標記一下直接關聯的對象所以時間非常短。
        2)併發標記  從GC Roots直接關聯的對象開始標記整個對象圖,所以非常耗時,但是不需要暫停用戶線程,也因此需要重新標記修正併發標記期間的對象變動
        3)重新標記 需要STW 因爲併發標記期間用戶線程和GC線程併發工作,會引起對象引用發生變化CMS採用增量更新(解決的漏標而被清除的問題)方式解決,停頓時間比初始標記時間要長但是遠比並發標記時間短。
        4)併發清除 清理掉標記階段判斷死亡的對象,因爲不需要移動存活對象,所以可以與用戶線程併發。​​        

         CMS收集器默認啓用的回收線程數是(處理器核心數+3)/4,因此處理器核心數在4或者4以上時,垃圾回收線程只佔處理器資源的25%不到,並且會隨着核心數增加而降低,但是當處理器核心數小於4時,對用戶程序影響就會很大。
        CMS收集器無法清理浮動垃圾(併發標記階段收集線程和用戶線程併發產生的垃圾),只能等下一次垃圾回收時清理。併發標記期間也需要留部分內存給用戶線程,所以CMS收集器不是等老年代滿了之後執行垃圾清理的,可以提高CMS收集器所使用的空間佔老年代空間比例-XX:+CMSInitiatingOccupancyFraction來提高CMS的觸發百分比,降低內存回收頻率,獲取更好的性能;比例太高了會發CMS收集器運行期間無法給新對象分配內存,而併發失敗,虛擬機就會啓動備用方案:臨時啓動Serial Old收集器重新對老年代垃圾收集,但是這樣停頓時間就更長了。所以-XX:+CMSInitiatingOccupancyFraction 的值需要權衡設置。
        CMS收集器是基於標記清除算法,所以會
產生內存碎片。CMS收集器提供了-XX:+UseCMSCompactAtFullCollection開關參數(默認開啓,JDK9開始廢棄),用於不得已進行Full GC時開啓內存整理,內存整理過程要移動存活對象,就使停頓時間變長,還提供了一個-XX:+CMSFullGCsBegoreCompaction(JDK9開始廢棄),作用是CMS收集器執行(參數值)次不整理老年代空間的Full GC後,下一次進入Full GC進行內存整理(默認0 每次都進行內存整理)。

7.Garbage First收集器
       
Garbage First(簡稱G1)收集器,是
面向局部收集的設計思想,基於Region的內存佈局將堆內存劃分爲多個大小相等的獨立區域(Region),每個Region根據需要扮演新生代的Eden、Survivor或年老代。Region中有Humongous區域,用來存儲大對象,G1收集器認爲對象大於Region一半就是大對象。Region大小可以通過-XX:+G1HeapRegionSize設定。
        G1收集器單次最小回收單元爲Region,每次收集的內存空間都是Region的整數倍,根據Region中的
垃圾回收價值(回收所獲得的空間大小和回收所需時間的和)大小排序,根據用戶設置的收集停頓時間-XX:+MaxGCPauseMillis),優先處理回收價值大的Region,不會在整個堆中進行全區域收集,所以可以建立預測停頓時間模型。
G1收集器仍然需要解決的問題:

        1)Region中的對象跨Region引用問題
                G1收集器有自己的記憶集,一種
雙向卡表結構,額外耗費大約堆內存的10%到20%
        2)併發標記階段如何保證收集線程和用戶線程互不干擾?
                1.用戶線程改變對象引用關係
                        
原始快照方式(重新掃描掃描開始之前記錄的原始快照)。
                2.垃圾回收過程中用戶線程新對象內存分配問題
                        Region中設計兩個
TAMS指針,將併發回收過程中分配的新對象,存儲到兩指針指的位置上,給予默認標記,並讓他們存活。
        3)如何建立可靠的停頓時間預測模型
                Region統計最新的回收價值,準確的預測現在回收那些Region能夠在滿足停頓時間要求內獲得最大的額吞吐量。


                
G1收集器運行階段分爲以下四個步驟:

1.初始標記 標記GC Roots直接關聯的對象,並修改TAMS指針的值,讓下一階段用戶線程併發時能正確的給新對象分配內存,需要STW但是時間很短。
2.併發標記 從GC Roots開始遞歸掃描整個堆裏的對象圖,耗時較長,用戶線程併發運行。
3.最終標記 需要STW,對原始快照的引用變動對象進行處理
4.篩選回收 根據用戶設置的停頓時間來選擇回收的Region,然後將存活的對象複製到空的Region中,最後清除掉整個Region,涉及內存整理需要STW,但是有多條收集線程並行完成

        由以上可以看出G1並不是純粹的追求停頓時間短,而是在設定的停頓時間內,獲得儘可能大的額吞吐量 。
        所以G1收集器的關鍵是根據不同的情境,設置合適的停頓時間值,從而達到停頓時間和吞吐量的最優。一般停頓時間設置在一兩百毫秒到兩三百毫秒之間。
        G1收集器衝整體上看是屬於標記整理算法,但從局部(兩個Region之間)看屬於標記複製算法,無論哪種算法都意味着G1不會產生內存碎片。但是G1收集器運行需要消耗更多的資源。G1收集器寫後屏障維護自己卡表,寫前屏障來跟蹤併發標記時指針變化。

8.GZC收集器後續更新

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