垃圾回收(Garbage Collection,GC),顧名思義就是釋放垃圾佔用的空間,防止內存泄露。有效的使用可以使用的內存,對內存堆中已經死亡的或者長時間沒有使用的對象進行清除和回收。
四大垃圾回收算法思想
-
引用記數
引用計數算法(Reachability Counting)是通過在對象頭中分配一個空間來保存該對象被引用的次數(Reference Count)。如果該對象被其它對象引用,則它的引用計數加1,如果刪除對該對象的引用,那麼它的引用計數就減1,當該對象的引用計數爲0時,那麼該對象就會被回收。 -
標記清除
標記-清除算法(Mark-Sweep)是最基礎的一種垃圾回收算法,它分爲 2 部分,先把內存區域中的這些對象進行標記,哪些屬於可回收標記出來,然後把這些垃圾拎出來清理掉。
-
複製算法
複製算法(Copying)是在標記清除算法上演化而來,解決標記清除算法的內存碎片問題。
-
標記整理
標記-整理算法(Mark-Compact)的標記過程仍然與標記清除算法一樣,但後續步驟不是直接對可回收對象進行清理,而是讓所有存活的對象都向一端移動,再清理掉端邊界以外的內存區域。
四種主要垃圾收集器
-
串行垃圾回收器(Serial)
它爲單線程環境設計且只使用一個線程進行垃圾回收,會暫停所有用戶線程,所以不適合服務器環境。 -
並行垃圾回收器(Parallel)
多個垃圾收集線程並行工作,此時用戶線程是暫停的,適用於科學計算/大數據處理等弱交互場景。 -
併發垃圾回收器(CMS)
用戶線程和垃圾收集線程同時執行(不一定並行,可能交替執行),不需要停頓用戶線程,互聯網公司多用它,適用對響應時間有要求的場景。 -
G1
是一種服務端的垃圾收集器,應用在多處理器和大容量內容環境中,在實現高吞吐量的同時,儘可能的滿足垃圾收集暫停時間的要求。具有以下特性:- 像CMS收集器一樣,能與應用程序線程併發執行
- 整理空間空間更快
- 需要更多時間來預測GC停頓時間
- 不希望犧牲大量的吞吐性能
- 不需要更大的Java Heap
七種垃圾回收
新生代
-
串行GC(Serial、Serial Copying)
一個單線程的收集器,在進行垃圾收集時候,必須暫停其它所有的工作線程直到它收集結束。
串行收集器是最古老的、最穩定以及效率最高的收集器,只使用一個線程去回收但其在進行垃圾收集過程中可能會產生較長的停頓(STW “Stop-The-Word”狀態)。雖然在收集垃圾過程中需要暫停所有其他的工作線程,但是它簡單高效,對於限定單個CPU環境來說,沒有線程交互的開銷可以獲得最高的單線程垃圾收集效率,因此Serial垃圾收集器依然是java虛擬機運行在Client模式下默認的新生代垃圾收集器。對應JVM參數:
-XX:+UseSerialGC
開啓後會使用:Serial(Young區用)+Serial Old(Old區用)的收集器組合。
表示:新生代、老年代都會使用串行回收收集器,新生代使用複製算法,老年代使用標記-整理算法 -
並行GC(ParNew)
使用多線程進行垃圾回收,在垃圾收集時,會Stop-the-world暫停所有的工作線程直到它收集結束。
ParNew收集器其實就是Serial收集器新生代的並行多線程版本,最常見的應用場景是配合老年代的CMS GC工作,其餘的行爲和Serial收集器完全一樣,ParNew垃圾收集器在垃圾收集過程中同樣也要暫停其它的工作線程。它是很多java虛擬機運行在Server模式下新生代的默認垃圾收集器。常用對應JVM參數:
-XX:+UseParNewGC
啓用ParNew收集器,隻影響新生代的收集,不影響老年代。開啓上述參數後會使用:ParNew(Young區用)+Serial Old的收集器組合,新生代使用複製算法,老年代採用標記-整理算法。 -
並行回收GC(Parallel、Parallel Scavenge)
就是串行收集器在新生代和老年代的並行化
Parallel Scavenge
收集器類似ParNew
也是一個新生代收
集器,使用複製算法,也是一個並行的多線程的垃圾收集器,俗稱吞吐量優先收集器。它重點關注的是:
可控制的吞吐量 (Thoughput=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間),也即比如程序運行100分鐘,垃圾收集時間1分鐘,吞吐量就是99%)。高吞吐量意味着高效利用CPU時間,它多用於在後臺運算而不需要太多交互的任務。
自適應調節策略也是ParallelScavenge收集器與ParNew收集器一個重要區別。自適應調節策略:虛擬機會根據當前系統的運行情況收集性能監控信息,動態調控這些參數以提供最合適的停頓時間-XX:MaxGCPauseMills
或最大的吞吐量。
老年代
-
並行GC(Serial Old、Serial MSC)
Serial Old是Serial垃圾收集器老年代版本,它同樣是個單線程的收集器,使用標記-整理算法,這個收集器也主要運行在Client默認的java虛擬機默認的老年代的垃圾收集器。 -
串行GC(Parallel Old、Parallel MSC)
Parallel Old收集器是Parallel Scavenge的老年代版本,使用多線程的標記-整理算法,Parallel Old收集器在JDK1.6纔開始提供。
在JDK1.6之前,新生代使用ParallelScavenge收集器只能搭配老年代的Serial Old收集器,只能保證新生代的吞吐量優先,無法保證整體的吞吐量。在JDK1.6之前(Parallel Scavenge + Serial Old)。
Parallel Old正是爲了在老年代同樣提供吞吐量優先的垃圾收集器,如果系統對吞吐量要求比較高,JDK1.8後可以優先考慮新生代Parallel Scavenge和老年代Parallel Old收集器搭配策略。在JDK1.8及以後(Parallel Scavenge + Parallel Old)JVM常用參數:
-XX:UserParallelOldGC
使用Parallel Old收集器,設置該參數後,新生代Parallel + 老年代Parallel Old -
CMS收集器(Concurrent Mark Sweep:併發標記清除)
是一種以獲取最短停頓時間爲目標的收集器。適合應用在互聯網站或B/S系統的服務上,這類應用尤其重視服務器的響應速度,希望系統停頓時間最短。CMS非常適合推內存大、CPU核數多的服務器端應用,也是G1出現之前大型應用的首選收集器。
CMS併發標記清除,併發收集低停頓,併發指的是與用戶線程一起執行。開啓該收集器的JVM參數:
-XX:+UseConcMarkSweepGC
開啓開啓該參數後自動將-XX:+UseParNewGC
打開。
開啓該參數後,使用ParNew(Young區用)+CMS(Old區用)+Serial Old有收集器組合,Serial Old將作爲CMS出錯的後備收集器。四步過程:
- 初始標記(CMS initial mark):只有標記一起GC Roots能直接關聯的對象,速度很快,仍然需要暫停所有的工作線程。
- 併發標記(CMS concurrent mark)和用戶線程一起:進行GC Roots跟蹤過程,和用戶線程一起工作,不需要暫停工作線程。主要標記過程,標記全部對象。
- 重新標記(CMS remark):爲了修正在併發標記期間,因用戶程序繼續運行而導致標記產生變動的那一部分對象的標記記錄,仍然需要暫停所有的工作線程。
- 併發清除(CMS concurrent sweep)和用戶線程一起:清除GC Roots不可達對象,和用戶線程一起工作,不需要暫停工作線程。基於標記結果,直接清理對象。
所以總體上來看CMS收集器的內存回收和用戶線程是一起併發地執行。
優點:併發收集低停頓
缺點:併發執行對CPU資源壓力。採用的標記清除算法會導致大量碎片。
核心思想是將整個推內個區域劃分成大小相同的子區域(Region),在JVM啓動時會自己設置這些區域的大小,在推的使用上,G1並不要求對象的存儲一定是物理連續的只要邏輯上連續即可,每個分區也不會固定地爲某個代服務,可按需在年輕代和老年代之間切換。啓動時可以通過參數:-XX:G1HeapRegionSize=n
可指定分區大小(1MB~32MB且必須是2的冪),默認將整個推劃分爲2048個分區。
大小範圍在1MB~32MB,最多能設置2048個區域,也即能支持的最大內存爲:32MB*2048=64G
G1
區域化內存碎片Region,整體爲了一些不連續的內存區域,避免了全內存的GC操作。
參數 | 新生代垃圾收集器 | 新生代算法 | 老年代垃圾收集器 | 老年代算法 |
---|---|---|---|---|
-XX:+UseSerialGC | SerialGC | 複製 | SerialOldGC | 標整 |
-XX:+UseParNewGC | ParNew | 複製 | SerialOldGC | 標整 |
-XX:+UseParallelGC、-XX+UseParallelOldGC | Parallel[Scavenge] | 複製 | Parallel Old | 標整 |
-XX:+UseConcMarkSweepGC | ParNew | 複製 | CMS + Serial Old的收集器組合(Serial Old作爲CMS出錯的後備收集器) | 標整 |
-XX:+UseGqGC | G1整體上採用標記-整理算法 | 局部通過複製算法,不會產生內存碎片 | $1600 |