垃圾收集器就是內存回收的具體實現。這裏討論的收集器基於JDK 1.7 Update 14以後的HotSpot虛擬機(在這個版本中正式提供了商用的G1收集器,以前G1仍處於實驗狀態),這個虛擬機包含的全部收集器。java
通常來講如下是對應JDK版本的垃圾收集器,絕大多數開發者也不會去從新設置。 jdk1.7 默認垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)算法
jdk1.8 默認垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)bash
jdk1.9 默認垃圾收集器G1服務器
如何查看? cmd執行命令: java -XX:+PrintCommandLineFlags -version 多線程
下面來分別對每個收集器進行一一介紹。併發
Serial收集器是最基本、發展歷史最悠久的收集器。這個收集器是一個單線程的收集器,但它的「單線程」的意義並不只僅說明它只會使用一個CPU或一條收集線程去完成垃圾收集工做,更重要的是在它進行垃圾收集時,必須暫停其餘全部的工做線程,直到它收集結束。但這項工做其實是由虛擬機在後臺自動發起和自動完成的,在用戶不可見的狀況下把用戶正常工做的線程所有停掉,這對不少應用來講都是難以接受的。post
優勢: 簡單高效,單線程,獨佔式的垃圾回收。 缺點: 在內部自發的進行垃圾收集的,一旦開啓,必須暫停其餘線程。性能
如何設置:優化
-XX:+UseSerialGC
複製代碼
ParNew收集器其實就是Serial收集器的多線程版本,除了使用多條線程進行垃圾收集以外,其他行爲包括Serial收集器可用的全部控制參數(例如:-XX:SurvivorRatio、 —XX:PretenureSizeThreshold、-XX:HandlePromotionFailure等)、收集算法、Stop The World、對象分配規則、回收策略等都與Serial收集器徹底同樣,在實現上,這兩種收集器也共用了至關多的代碼。網站
(CMS這款收集器是HotSpot虛擬機中第一款真正意義上的併發(Concurrent)收集器,它第一次實現了讓垃圾收集線程與用戶線程(基本上)同時工做。)後面介紹
ParNew收集器在單CPU的環境中絕對不會有比Serial收集器更好的效果,甚至因爲存在線程交互的開銷,該收集器在經過多線程技術實現的兩個CPU的環境中都不能百分之百地保證能夠超越Serial收集器。可使用-XX:ParallelGCThreads參數來限制垃圾收集的線程數。可是隨着CPU數量的變多,對於GC時系統資源的有效利用仍是頗有好處的。它默認開啓的收集線程數與CPU的數量相同。
理解兩個概念:
● 並行(Parallel):指多條垃圾收集線程並行工做,但此時用戶線程仍然處於等待狀態。
●併發(Concurrent):指用戶線程與垃圾收集線程同時執行(但不必定是並行的,可能會交替執行),用戶程序在繼續運行,而垃圾收集程序運行於另外一個CPU上。
如何設置:
-XX:+UseParNewGC
複製代碼
Parallel Scavenge收集器的特色是它的關注點與其餘收集器不一樣,CMS等收集器的關注點是儘量地縮短垃圾收集時用戶線程的停頓時間,而Parallel Scavenge收集器的目標則是達到一個可控制的吞吐量(Throughput)。所謂吞吐量就是CPU用於運行用戶代碼的時間與CPU總消耗時間的比值,即吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間),虛擬機總共運行了100分鐘,其中垃圾收集花掉1分鐘,那吞吐量就是99%。
Parallel Scavenge收集器提供了兩個參數用於精確控制吞吐量,分別是控制最大垃圾收集停頓時間的-XX:MaxGCPauseMillis參數
MaxGCPauseMillis參數容許的值是一個大於0的毫秒數,收集器將盡量地保證內存回收花費的時間不超過設定值。不過你們不要認爲若是把這個參數的值設置得稍小一點就能使得系統的垃圾收集速度變得更快,GC停頓時間縮短是以犧牲吞吐量和新生代空間來換取的:系統把新生代調小一些,收集300MB新生代確定比收集500MB快吧,這也直接致使垃圾收集發生得更頻繁一些,原來10秒收集一次、每次停頓100毫秒,如今變成5秒收集一次、每次停頓70毫秒。停頓時間的確在降低,但吞吐量也降下來了。
直接設置吞吐量大小的-XX:GCTimeRatio參數。
GCTimeRatio參數的值應當是一個大於0且小於100的整數,也就是垃圾收集時間佔總時間的比率,至關因而吞吐量的倒數。若是把此參數設置爲19,那容許的最大GC時間就佔總時間的5%(即1/(1+19)),默認值爲99,就是容許最大1%(即1/(1+99))的垃圾收集時間。
Parallel Scavenge收集器還有一個參數-XX:+UseAdaptiveSizePolicy值得關注。這是一個開關參數,當這個參數打開以後,就不須要手工指定新生代的大小(-Xmn)、Eden與Survivor區的比例(-XX:SurvivorRatio)、晉升老年代對象年齡(-XX:PretenureSizeThreshold)等細節參數了,虛擬機會根據當前系統的運行狀況收集性能監控信息,動態調整這些參數以提供最合適的停頓時間或者最大的吞吐量,這種調節方式稱爲GC自適應的調節策略。
注:若是讀者對於收集器運做原來不太瞭解,手工優化存在困難的時候,使用Parallel Scavenge收集器配合自適應調節策略,把內存管理的調優任務交給虛擬機去完成將是一個不錯的選擇。只須要把基本的內存數據設置好(如-Xmx設置最大堆),而後使用MaxGCPauseMillis參數(更關注最大停頓時間)或GCTimeRatio(更關注吞吐量)參數給虛擬機設立一個優化目標,那具體細節參數的調節工做就由虛擬機完成了。 如何設置:
-XX:+UseParallelGC
複製代碼
這個收集器的主要意義也是在於給Client模式下的虛擬機使用。若是在Server模式下,那麼它主要還有兩大用途:一種用途是在JDK 1.5以及以前的版本中與Parallel Scavenge收集器搭配使用[1],另外一種用途就是做爲CMS收集器的後備預案,在併發收集發生ConcurrentMode Failure時使用。
如何設置: 跟上一個同樣
Parallel Old是Parallel Scavenge收集器的老年代版本,使用多線程和「標記-整理」算法。這個收集器是在JDK 1.6中才開始提供的。
在此以前,新生代的Parallel Scavenge收集器一直處於比較尷尬的狀態。緣由是,若是新生代選擇了Parallel Scavenge收集器,老年代除了Serial Old(PS MarkSweep)收集器外別無選擇(還記得上面說過Parallel Scavenge收集器沒法與CMS收集器配合工做嗎?)。因爲老年代Serial Old收集器在服務端應用性能上的「拖累」,使用了Parallel Scavenge收集器也未必能在總體應用上得到吞吐量最大化的效果,因爲單線程的老年代收集中沒法充分利用服務器多CPU的處理能力,在老年代很大並且硬件比較高級的環境中,這種組合的吞吐量甚至還不必定有ParNew加CMS的組合「給力」。
直到Parallel Old收集器出現後,「吞吐量優先」收集器終於有了比較名副其實的應用組 合,在注重吞吐量以及CPU資源敏感的場合,均可以優先考慮Parallel Scavenge加Parallel Old收集器。
如何設置:
-XX:+UseParallelOldGC
複製代碼
CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間爲目標的收集器。目前很大一部分的Java應用集中在互聯網站或者B/S系統的服務端上,這類應用尤爲重視服務的響應速度,但願系統停頓時間最短,以給用戶帶來較好的體驗。CMS收集器就很是符合這類應用的需求。從名字(包含「Mark Sweep」)上就能夠看出,CMS收集器是基於「標記—清除」算法實現的,它的運做過程相對於前面幾種收集器來講更復雜一些,整個過程分爲4個步驟,包括:
初始標記(CMS initial mark) 併發標記(CMS concurrent mark) 預清理 從新標記(CMS remark) 併發清除(CMS concurrent sweep) 併發重置
其中,初始標記、從新標記這兩個步驟仍然須要「Stop The World」。初始標記僅僅只是標記一下GC Roots能直接關聯到的對象,速度很快,併發標記階段就是進行GC RootsTracing的過程,而從新標記階段則是爲了修正併發標記期間因用戶程序繼續運做而致使標記產生變更的那一部分對象的標記記錄,這個階段的停頓時間通常會比初始標記階段稍長一些,但遠比並發標記的時間短。
因爲整個過程當中耗時最長的併發標記和併發清除過程收集器線程均可以與用戶線程一塊兒工做,因此,從整體上來講,CMS收集器的內存回收過程是與用戶線程一塊兒併發執行的。
主要優勢在名字上已經體現出來了:併發收集、低停頓。是一個並行垃圾處理器,用戶線程與垃圾回收線程一塊兒運行(不包括前兩個標記)。
缺點: 1.CMS收集器沒法處理浮動垃圾 2.使用的是「標記—清除」算法實現的收集器,容易產生大量空間碎片。 3.對CPU資源比較敏感,,面向併發設計的程序對CPU資源都很是敏感的,受CPU的數量影響會比較大。數量越小,影響越大。
如何設置開啓
-XX:+UseConcMarkSweepGC //使用cms垃圾處理器
-XX:-CMSPrecleaningEnabled //不進行預處理
-XX:ConcGCThreads 或者 -XX:ParallelCMSThreads 參數手工預約併發線程數
-XX:CMSInitiatingOccupancyFraction 控制達到必定闕值後,進行CMS回收,默認是68.
若是應用程序內存增加很快,則須要,應該下降這個闕值。
若是增加很慢,則須要設置一個較大的值。大的闕值能夠有效下降觸發垃圾回收的頻率
因爲CMS是使用標記清楚算法,每次清楚後會形成大量的內存碎片,離散的可用空間沒法分配到較大的對象,
這種狀況下,即便對內存有較大剩餘空間,也有可能被迫進行一次垃圾回收,所以可使用如下參數
-XX:+UseCMSCompactAtFullCollection 開關可使CMS在垃圾回收完成後進行一次內存碎片整理,壓縮整理
-XX:CMSFullGCsBeforeCompaction 能夠用於設定多少次垃圾回收後,進行一次內存整理。
複製代碼
在使用CMS回收時,若是須要使用Perm區,那麼默認狀況下,仍是須要觸發一次fullGC
-XX:+CMSClassUnloadingEnabled 開關 使用後,若是條件容許,系統會使用CMS的機制回收Perm區的class日誌。
複製代碼
G1是一款面向服務端應用的垃圾收集器。HotSpot開發團隊賦予它的使命是(在比較長期的)將來能夠替換掉JDK 1.5中發佈的CMS收集器。與其餘GC收集器相比,G1具有以下特色。
並行與併發、分代收集的垃圾收集算法、可預測的停頓、空間整合。
並行與併發:G1能充分利用多CPU、多核環境下的硬件優點,使用多CPU(CPU或者CPU核心)來縮短Stop-The-World停頓的時間,部分其餘收集器本來須要停頓Java線程執行的GC動做,G1收集器仍然能夠經過併發的方式讓Java程序繼續執行。
分代收集:與其餘收集器同樣,分代概念在G1中依然得以保留。雖然G1能夠不須要其餘收集器配合就能獨立管理整個GC堆,但它可以採用不一樣的方式去處理新建立的對象和已經存活了一段時間、熬過屢次GC的舊對象以獲取更好的收集效果。
空間整合:與CMS的「標記—清理」算法不一樣,G1從總體來看是基於「標記—整理」算法實現的收集器,從局部(兩個Region之間)上來看是基於「複製」算法實現的,但不管如何,這兩種算法都意味着G1運做期間不會產生內存空間碎片,收集後能提供規整的可用內存。這種特性有利於程序長時間運行,分配大對象時不會由於沒法找到連續內存空間而提早觸發下一次GC。
可預測的停頓:這是G1相對於CMS的另外一大優點,下降停頓時間是G1和CMS共同的關注點,但G1除了追求低停頓外,還能創建可預測的停頓時間模型,能讓使用者明確指定在一個長度爲M毫秒的時間片斷內,消耗在垃圾收集上的時間不得超過N毫秒,這幾乎已是實時Java(RTSJ)的垃圾收集器的特徵了。
想要更深刻的理解G1,能夠去該地址看: juejin.im/post/5d6fbd…
來自《深刻理解JVM虛擬機》JVM高級特性與最佳實現。
《實戰java虛擬機》
複製代碼