整理概括HotSpot中的GC收集器相關性能,算法使用,GC過程和相互搭配。須要先明確一個觀點:GC收集器根本上來講沒有絕對的優劣,咱們只能根據具體場景選擇最適合的GC組合,而不是選擇一個完美的GC組合。
介紹以前,先須要瞭解兩個名詞概念:java
單線程有兩個方面含義: 一方面,serial收集器只使用一個CPU或一條收集線程進行GC; 另外一方面,serial進行GC時,須要暫停其餘全部的工做線程直到垃圾回收結束(Stop The World)
一方面,Serial收集器只是用一條GC線程去執行收集任務;另外一方面,Serial收集器進行收集時,必須暫停其餘全部的工做線程(Stop The World),直到收集結束。
吞吐量 = 運行用戶代碼時間 / (運行用戶代碼時間 + 垃圾收集時間)CMS等收集器關注點是儘量縮短垃圾收集時用戶線程停頓的時間,Parallel Scavenge收集器關注的是達到一個可觀的吞吐量。
停頓時間短適合須要和用戶交互多的程序;高吞吐量能夠高效利用CPU使用率,適合在後臺運算而不須要太多交互的任務。算法Parallel Scavenge收集器提供兩個參數用於控制吞吐量:
-XX:MaxGCPauseMillis :最大垃圾收集停頓時間。值與新生代空間和吞吐量成反比。
-XX:GCTimeRatio:吞吐量大小。值能夠理解爲正常運行時間相對垃圾收集時間的倍數,即正常運行時間/垃圾回收時間,默認值爲99,即容許最大1%(1/1+99)的垃圾收集時間。多線程GC自適應調節策略:
Parallel Scavenge收集器還有一個參數-XX:+UseAdaptiveSizePolicy設置當前系統是否使用自適性系統參數調節,當開關打開時,系統不須要手動設置新生代大小、Eden和Survivor比例、晉升老年代對象年齡。併發
![Parallel Scavenge/Parallel Old
收集器運行過程.png](https://upload-images.jianshu...佈局
HotSpot第一款真正意義上的併發收集器。第一次實現了GC線程和工做線程(基本上)同時工做性能
只標記GC Roots能直接關聯到的對象,須要Stop The World(STW)。spa
進行GC Roots引用鏈追蹤,標記全部有關聯的對象。這時GC線程能和用戶線程同時工做(用書上的形容是:真正的實現了你邊丟垃圾,你媽媽邊打掃衛生)。線程
修正併發標記時,發生引用關係變化的那部分對象的引用,須要Stop The World。設計
使用併發-清除算法對垃圾對象進行清除。3d
CMS清除內部狀態,爲下次GC作準備
整個過程當中耗時最長的併發標記和併發清除過程收集器線程均可以與用戶線程一塊兒工做,因此,從整體上來講,CMS收集器的內存回收過程是與用戶線程一塊兒併發執行的
雖然併發標記和併發清除時,不會致使用戶線程停頓,但因爲佔用一部分線程資源而致使應用程序速度變慢。CMS默認啓動的回收線程是(CPU數量 + 3) / 4
CMS收集器進行到併發清除階段時,因爲併發執行,系統仍然會產生一些垃圾,這些垃圾產生在標記以後,因此須要等待下次GC再清除他們,這些垃圾叫作「浮動垃圾」。正因如此,CMS收集器對老年代須要預留一部分空間提供併發收集時的程序運做使用。
CMS經過設置參數(-XX:CMSInitiatingOccupancyFraction)用來表示老年代使用多少空間時,激活CMS。設置這個參數須要有兩方面的考量:一方面,當參數值設置太低,觸發CMS的GC次數會變多,下降性能;另外一方面,當參數值設置太高,剩餘空間不足以存儲產生的浮動垃圾,系統會報「Concurrent Mode Failure」,系統將啓動預備方案:使用Serial Old收集器進行老年代的垃圾收集,這樣致使耗時更多,影響性能。
併發-清除算法將產生大量空間碎片,當大對象進入內存時,會因爲沒有足夠的連續內存空間分配而提早觸發Full GC。
爲此設計者提供了兩個參數。-XX:+UseCMSCompactAtFullCollection開關參數控制CMS收集器在須要進行Full GC時,是否開啓內存碎片整理過程(默認是開啓的)。-XX:CMSFullGCsBeforeCompaction設置執行多少次不壓縮內存空間的Full GC後,進行一次帶壓縮的Full GC(默認爲0,即每次進入Full GC都要進行碎片整理)。
代替Parallel Scavenge和Parallel Old組合收集器,成爲JDK1.9服務端模式下默認垃圾收集器。設計初衷是創建起「停頓時間模型」的收集器,即支持指定在一個長度爲M毫秒的時間片斷內,消耗在GC上的時間不超過N毫秒這樣的目標。
可預測的停頓:G1支持使用者設置在M時間中停頓N秒。G1在後臺維護一個列表用於記錄每一個Region裏面的垃圾回收的價值(回收得到的空間大小和回收所需時間),根據用戶設置的時間,制定回收計劃,優先回收價值大的區域(Garbage-First的由來)。
以前的垃圾收集器的垃圾收集對象爲整個新生代(Minor GC)、整個老年代(Major GC)或整個Java堆(Full GC)。而G1面向堆內存的任何部分來組成回收集進行垃圾回收,衡量標準再也不是內存屬於哪一個年代,而是哪塊內存中存放的垃圾數量最多,回收收益最大。
爲了實現這一收集目標,G1的堆內存佈局開創了基於Region的堆內存佈局。
G1雖仍然遵循分代收集,可是不一樣於以前的收集器將年輕代、年老代和元空間按照固定大小以及固定數量進行區域劃分,而是將連續的Java堆劃分爲大小相等的若干區域——Region,每一個區域根據須要能夠是任何年代的對象,各個年代沒有物理連續只有邏輯上的連續。收集器就能夠根據扮演不一樣年代的Region採用不一樣的回收策略。
除此以外,增長了一個區域——Humongous區域,用於存儲巨型對象,若是一個對象佔用空間超過Region容量的通常,G1則認爲這是一個巨型對象(Region取值範圍爲1MB~32MB,應爲2的N次冪,經過-XX:G1HeapRegionSize設定)。若是一個Region裝不下一個巨型對象,則會尋找連續的Humongous分區來存儲,有時爲找到連續的H分區,有時會觸發Full GC。H區域的出現避免了短時間存在的巨型對象對GC形成負面影響。G1大多數行爲把H區域當作老年代看待。
有了新的垃圾收集思想和堆內存佈局,「可預測的停頓時間模型」得以實現:
只標記GC Roots能直接關聯到的對象,修改TAMS指針值,讓下個階段能正確的在可用Region中分配對象。須要停頓線程,但借用Minor GC的時候同步完成,沒有額外停頓。
從GC Roots進行可達性遍歷,對整個Java堆的對象圖進行掃描,找出回收對象。這個階段能夠和用戶線程併發執行。還要從新處理SATB記錄下的在併發時引用有變更的對象。
處理併發階段後遺留下來的少許的SATB 記錄,須要短暫暫停。
SATB(Snapshot At The Beginning):簡單地說就是初始標記階段和併發標記階段標記爲活的的對象就是活的。而後併發標記階段新增或者引用從新執行的對象也認爲是活的。其餘的就是死的
更新Region統計數據,對各Region回收價值和成本進行排序,根據用戶指望停頓時間來指定回收計劃。回收過程將決定回收的那一部分Region的存活對象複製到空的Region中,而後清理掉舊的Region的所有空間。須要停頓用戶線程。
由回收過程能夠看出G1並不是純粹追求低延遲,而是在延遲可控的狀況下得到儘量高的吞吐量。
jdk1.7 Parallel Scavenge(新生代)+Parallel Old(老年代)
jdk1.8 Parallel Scavenge(新生代)+Parallel Old(老年代)
jdk1.9 G1