今天2B哥跟各位牛人分享JVM相關的知識點,今天重點介紹CMS和G1收集器,某些小哥哥就問爲何不講講其餘收集器?按面試經驗來講,這兩種收集器問的最多,固然優先講這兩種呀,可是,我說可是,若是你關注我還能看到更多關於JVM的知識,保證讓你收穫滿滿,廢話很少說,直接上乾貨。java
收集器於JVM堆的關係
複製代碼
JVM參數:-XX:+UseConcMarkSweepGC面試
CMS(Concurrent Mark Sweep)收集器是 HotSpot 虛擬機第一款真正意義上的併發收集器,它第一次實現了讓垃圾收集線程與用戶線程(基本上)同時工做。-XX:+UseConcMarkSweepGC來開啓CMS算法
從名字中的Mark Sweep這兩個詞能夠看出,CMS 收集器是一種 「標記-清除」算法實現的,它的運做過程相比於前面幾種垃圾收集器來講更加複雜一些。整個過程分爲四個步驟:bash
初始標記: 暫停全部的其餘線程,並記錄下直接與gc root 相連的對象,速度很快 ;服務器
併發標記: 同時開啓 GC 和用戶線程,用一個閉包結構去記錄可達對象。但在這個階段結束,這個閉包結構並不能保證包含當前全部的可達對象。由於用戶線程可能會不斷的更新引用域,因此 GC 線程沒法保證可達性分析的實時性。因此這個算法裏會跟蹤記錄這些發生引用更新的地方。閉包
從新標記: 從新標記階段就是爲了修正併發標記期間由於用戶程序繼續運行而致使標記產生變更的那一部分對象的標記記錄,這個階段的停頓時間通常會比初始標記階段的時間稍長,遠遠比並發標記階段時間短併發
併發清除: 開啓用戶線程,同時 GC 線程開始對爲標記的區域作清掃。性能
-Xmx200M -Xmn50m -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+UseConcMarkSweepGC
複製代碼
輸出GC日誌以下: 測試
第一歩初始化標記
2020-01-05T21:52:05.411+0800: [GC (CMS Initial Mark) [1 CMS-initial-mark: 72364K(77824K)] 77550K(123904K), 0.0010600 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
第二歩併發標記
2020-01-05T21:52:05.412+0800: [CMS-concurrent-mark-start]
2020-01-05T21:52:05.416+0800: [CMS-concurrent-mark: 0.004/0.004 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
第三歩 預清理
2020-01-05T21:52:05.416+0800: [CMS-concurrent-preclean-start]
2020-01-05T21:52:05.417+0800: [CMS-concurrent-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
第四步 可被終止的預清理
[CMS-concurrent-abortable-preclean-start]
[CMS-concurrent-abortable-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
第五歩 從新標記
2020-01-05T21:52:05.417+0800: [GC (CMS Final Remark) [YG occupancy: 5185 K (46080 K)]2020-01-05T21:52:05.417+0800: [Rescan (parallel) , 0.0003800 secs]2020-01-05T21:52:05.417+0800: [weak refs processing, 0.0006763 secs]2020-01-05T21:52:05.418+0800: [class unloading, 0.0009691 secs]2020-01-05T21:52:05.419+0800: [scrub symbol table, 0.0019453 secs]2020-01-05T21:52:05.421+0800: [scrub string table, 0.0004832 secs][1 CMS-remark: 72364K(77824K)] 77550K(123904K), 0.0046503 secs] [Times: user=0.02 sys=0.00, real=0.00
第六歩清理
2020-01-05T21:52:07.174+0800: [CMS-concurrent-sweep-start]
2020-01-05T21:52:07.175+0800: [CMS-concurrent-sweep: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
第七歩重置
2020-01-05T21:52:07.175+0800: [CMS-concurrent-reset-start]
複製代碼
總結CMS收集器 處理過程有7個步驟:spa
主要優勢:併發收集、低停頓。
可是它有下面三個明顯的缺點:
· 對 CPU 資源敏感;
· 沒法處理浮動垃圾;
· 它使用的回收算法-**「標記-清除」**算法會致使收集結束時會有大量空間碎片產生(不作整理 -XX:CMSFullGCsBeforeCompaction=n 多少次以後作壓縮)。
JVM參數:-XX:+UseG1GC** **
G1 (Garbage-First) 是一款面向服務器的垃圾收集器,主要針對配備多顆處理器及大容量內存的機器. 以極高機率知足 GC 停頓時間要求的同時,還具有高吞吐量性能特徵.
被視爲 JDK1.7 中 HotSpot 虛擬機的一個重要進化特徵。它具有一下特色:
並行與併發:G1 能充分利用 CPU、多核環境下的硬件優點,使用多個 CPU(CPU 或者 CPU 核心)來縮短 Stop-The-World 停頓時間。部分其餘收集器本來須要停頓 Java 線程執行的 GC 動做,G1 收集器仍然能夠經過併發的方式讓 java 程序繼續執行。
G1將Java堆劃分爲多個大小相等的獨立區域(Region),JVM最多能夠有2048個Region**。**
通常Region大小等於堆大小除以2048,好比堆大小爲4096M,則Region大小爲2M,固然也能夠用參數"-XX:G1HeapRegionSize"手動指定Region大小,可是推薦默認的計算方式。
G1保留了年輕代和老年代的概念,但再也不是物理隔閡了,它們都是(能夠不連續)Region的集合。
默認年輕代對堆內存的佔比是5%,若是堆大小爲4096M,那麼年輕代佔據200MB左右的內存,對應大概是100個Region,能夠經過「-XX:G1NewSizePercent」設置新生代初始佔比,在系統運行中,JVM會不停的給年輕代增長更多的Region,可是最多新生代的佔比不會超過60%,能夠經過「-XX:G1MaxNewSizePercent」調整。年輕代中的Eden和Survivor對應的region也跟以前同樣,默認8:1:1,假設年輕代如今有1000個region,eden區對應800個,s0對應100個,s1對應100個。
一個Region可能以前是年輕代,若是Region進行了垃圾回收,以後可能又會變成老年代,也就是說Region的區域功能可能會動態變化。
G1垃圾收集器對於對象何時會轉移到老年代跟以前講過的原則同樣,惟一不一樣的是對大對象的處理,G1有專門分配大對象的Region叫Humongous區,而不是讓大對象直接進入老年代的Region中。在G1中,大對象的斷定規則就是一個大對象超過了一個Region大小的50%,好比按照上面算的,每一個Region是2M,只要一個大對象超過了1M,就會被放入Humongous中,並且一個大對象若是太大,可能會橫跨多個Region來存放。
Humongous區專門存放短時間巨型對象,不用直接進老年代,能夠節約老年代的空間,避免由於老年代空間不夠的GC開銷。
Full GC的時候除了收集年輕代和老年代以外,也會將Humongous區一併回收。
-Xmx200M -Xmn50m -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+UseG1GC
複製代碼
輸出日誌以下:
2020-01-08T13:19:36.126+0800: [GC pause (G1 Humongous Allocation)
第一步初始化標記
(young) (initial-mark), 0.0023830 secs]
[Parallel Time: 1.1 ms, GC Workers: 8]
[GC Worker Start (ms): Min: 162.8, Avg: 162.8, Max: 162.9, Diff: 0.0]
[Ext Root Scanning (ms): Min: 0.3, Avg: 0.5, Max: 0.9, Diff: 0.6, Sum: 3.8]
[Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Processed Buffers: Min: 0, Avg: 0.0, Max: 0, Diff: 0, Sum: 0]
[Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Object Copy (ms): Min: 0.0, Avg: 0.4, Max: 0.5, Diff: 0.5, Sum: 2.8]
[Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.2]
[Termination Attempts: Min: 1, Avg: 7.3, Max: 11, Diff: 10, Sum: 58]
[GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.1, Diff: 0.1, Sum: 0.3]
[GC Worker Total (ms): Min: 0.9, Avg: 0.9, Max: 0.9, Diff: 0.0, Sum: 7.2]
[GC Worker End (ms): Min: 163.7, Avg: 163.7, Max: 163.7, Diff: 0.0]
[Code Root Fixup: 0.0 ms]
[Code Root Purge: 0.0 ms]
[Clear CT: 0.6 ms]
[Other: 0.7 ms]
[Choose CSet: 0.0 ms]
[Ref Proc: 0.1 ms]
[Ref Enq: 0.0 ms]
[Redirty Cards: 0.5 ms]
[Humongous Register: 0.0 ms]
[Humongous Reclaim: 0.0 ms]
[Free CSet: 0.0 ms]
[Eden: 3072.0K(50.0M)->0.0B(49.0M) Survivors: 0.0B->1024.0K Heap: 30.5M(126.0M)->28.8M(126.0M)]
[Times: user=0.00 sys=0.00, real=0.00 secs]
2020-01-08T13:19:36.129+0800: [GC concurrent-root-region-scan-start]
2020-01-08T13:19:36.135+0800: [GC concurrent-root-region-scan-end, 0.0060575 secs]
第二步初始化標記
2020-01-08T13:19:36.135+0800: [GC concurrent-mark-start]
2020-01-08T13:19:36.135+0800: [GC concurrent-mark-end, 0.0000731 secs]
2020-01-08T13:19:36.135+0800: [GC remark 2020-01-08T13:19:36.135+0800: [Finalize Marking, 0.0002335 secs] 2020-01-08T13:19:36.136+0800: [GC ref-proc, 0.0000731 secs] 2020-01-08T13:19:36.136+0800: [Unloading, 0.0005882 secs], 0.0011010 secs]
[Times: user=0.00 sys=0.00, real=0.00 secs]
第三步篩選回收
2020-01-08T13:19:36.137+0800: [GC cleanup 49M->49M(126M), 0.0007633 secs]
[Times: user=0.00 sys=0.00, real=0.00 secs]
複製代碼
*初始標記(initial mark,STW):暫停全部的其餘線程,並記錄下gc roots直接能引用的對象,速度很快 ;
*併發標記(Concurrent Marking):同CMS的併發標記最終標記(Remark,STW):同CMS的從新標記
*篩選回收(Cleanup,STW):篩選回收階段首先對各個Region的回收價值和成本進行排序,根據用戶所指望的GC停頓時間(能夠用JVM參數 -XX:MaxGCPauseMillis指定)來制定回收計劃,好比說老年代此時有1000個Region都滿了,可是由於根據預期停頓時間,本次垃圾回收可能只能停頓200毫秒,那麼經過以前回收成本計算得知,可能回收其中800個Region恰好須要200ms,那麼就只會回收800個Region,儘可能把GC致使的停頓時間控制在咱們指定的範圍內。這個階段其實也能夠作到與用戶程序一塊兒併發執行,可是由於只回收一部分Region,時間是用戶可控制的,並且停頓用戶線程將大幅提升收集效率。不論是年輕代或是老年代,回收算法主要用的是複製算法,將一個region中的存活對象複製到另外一個region中,這種不會像CMS那樣回收完由於有不少內存碎片還須要整理一次,G1採用複製算法回收幾乎不會有太多內存碎片。
G1能充分利用CPU、多核環境下的硬件優點,使用多個CPU(CPU或者CPU核心)來縮短Stop-The-World停頓時間
缺點: G1從總體來看是基於「標記整理」算法實現的收集器;從局部上來看是基於**「複製」算法實現**的, 對內存使用率存在必定的「浪費」。
能看到這的都是牛人,麻煩幫忙點個贊關注下,下篇我繼續帶來CMS和G1實戰PK對比,圖形化對比看的更直觀。
加關注,不迷路。
複製代碼