7種jvm垃圾回收器,這次全部搞懂

公眾號_IT老哥發表於2020-10-21

前言

之前我們講解了jvm組成結構垃圾回收演算法等知識點,今天我們來講講jvm最重要的堆記憶體是如何使用垃圾回收器進行垃圾回收,並且如何使用命令去配置使用這些垃圾回收器。

堆記憶體詳解

image.png

上面這個圖大家應該已經很明白了吧。大家就可以理解成一個房子被分成了幾個房間,每個房間的作用不同而已,有的是嬰兒住的,有的是父母住的,有的是爺爺奶奶住的

  • 堆記憶體被劃分為兩塊,一塊的年輕代,另一塊是老年代
  • 年輕代又分為Edensurvivor。他倆空間大小比例預設為8:2,
  • 倖存區又分為s0s1。這兩個空間大小是一模一樣的,就是一對雙胞胎,他倆是1:1的比例

堆記憶體垃圾回收過程

第一步

新生成的物件首先放到Eden區,當Eden區滿了會觸發Minor GC

第二步

第一步GC活下來的物件,會被移動到survivor區中的S0區,S0區滿了之後會觸發Minor GC,S0區存活下來的物件會被移動到S1區,S0區空閒。

S1滿了之後在GC,存活下來的再次移動到S0區,S1區空閒,這樣反反覆覆GC,每GC一次,物件的年齡就漲一歲,達到某個值後(15),就會進入老年代

第三步

在發生一次Minor GC後(前提條件),老年代可能會出現Major GC,這個視垃圾回收器而定。

Full GC觸發條件

  • 手動呼叫System.gc,會不斷的執行Full GC
  • 老年代空間不足/滿了
  • 方法區空間不足/滿了

注意

們需要記住一個單詞:stop-the-world。它會在任何一種GC演算法中發生。stop-the-world 意味著JVM因為需要執行GC而停止應用程式的執行。

當stop-the-world 發生時,除GC所需的執行緒外,所有的執行緒都進入等待狀態,直到GC任務完成。GC優化很多時候就是減少stop-the-world 的發生。

回收哪些區域的物件

需要注意的是,JVM GC只回收堆記憶體方法區內的物件。而棧記憶體的資料,在超出作用域後會被JVM自動釋放掉,所以其不在JVM GC的管理範圍內。

堆記憶體常見引數配置

引數描述
-Xms堆記憶體初始大小,單位m、g
-Xmx堆記憶體最大允許大小,一般不要大於實體記憶體的80%
-XX:PermSize非堆記憶體初始大小,一般應用設定初始化200m,最大1024m就夠了
-XX:MaxPermSize非堆記憶體最大允許大小
-XX:NewSize(-Xns)年輕代記憶體初始大小
-XX:MaxNewSize(-Xmn)年輕代記憶體最大允許大小
-XX:SurvivorRatio=8年輕代中Eden區與Survivor區的容量比例值,預設為8,即8:1
-Xss堆疊記憶體大小
-XX:NewRatio=老年代/新生代設定老年代和新生代的大小比例
-XX:+PrintGCjvm啟動後,只要遇到GC就會列印日誌
-XX:+PrintGCDetails檢視GC詳細資訊,包括各個區的情況
-XX:MaxDirectMemorySize在NIO中可以直接訪問直接記憶體,這個就是設定它的大小,不設定預設就是最大堆空間的值-Xmx
-XX:+DisableExplicitGC關閉System.gc()
-XX:MaxTenuringThreshold垃圾可以進入老年代的年齡
-Xnoclassgc禁用垃圾回收
-XX:TLABWasteTargetPercentTLAB佔eden區的百分比,預設是1%
-XX:+CollectGen0FirstFullGC時是否先YGC,預設false

TLAB 記憶體

TLAB全稱是Thread Local Allocation Buffer即執行緒本地分配快取,從名字上看是一個執行緒專用的記憶體分配區域,是為了加速物件分配而生的。

每一個執行緒都會產生一個TLAB,該執行緒獨享的工作區域,java虛擬機器使用這種TLAB區來避免多執行緒衝突問題,提高了物件分配的效率。

TLAB空間一般不會太大,當大物件無法在TLAB分配時,則會直接分配到堆上。

引數描述
-Xx:+UseTLAB使用TLAB
-XX:+TLABSize設定TLAB大小
-XX:TLABRefillWasteFraction設定維護進入TLAB空間的單個物件大小,他是一個比例值,預設為64,即如果物件大於整個空間的1/64,則在堆建立
-XX:+PrintTLAB檢視TLAB資訊
-Xx:ResizeTLAB自調整TLABRefillWasteFraction閥值。

image.png

垃圾回收器總覽

image.png

新生代可配置的回收器:Serial、ParNew、Parallel Scavenge

老年代配置的回收器:CMS、Serial Old、Parallel Old

新生代和老年代區域的回收器之間進行連線,說明他們之間可以搭配使用。

新生代垃圾回收器

Serial 垃圾回收器

Serial收集器是最基本的、發展歷史最悠久的收集器。俗稱為:序列回收器,採用複製演算法進行垃圾回收

特點

序列回收器是指使用單執行緒進行垃圾回收的回收器。每次回收時,序列回收器只有一個工作執行緒。

對於並行能力較弱的單CPU計算機來說,序列回收器的專注性和獨佔性往往有更好的效能表現。

它存在Stop The World問題,及垃圾回收時,要停止程式的執行。

使用-XX:+UseSerialGC引數可以設定新生代使用這個序列回收器

ParNew 垃圾回收器

ParNew其實就是Serial的多執行緒版本,除了使用多執行緒之外,其餘引數和Serial一模一樣。俗稱:並行垃圾回收器,採用複製演算法進行垃圾回收

特點

ParNew預設開啟的執行緒數與CPU數量相同,在CPU核數很多的機器上,可以通過引數-XX:ParallelGCThreads來設定執行緒數。

它是目前新生代首選的垃圾回收器,因為除了ParNew之外,它是唯一一個能與老年代CMS配合工作的。

它同樣存在Stop The World問題

使用-XX:+UseParNewGC引數可以設定新生代使用這個並行回收器

ParallelGC 回收器

ParallelGC使用複製演算法回收垃圾,也是多執行緒的。

特點

就是非常關注系統的吞吐量,吞吐量=程式碼執行時間/(程式碼執行時間+垃圾收集時間)

-XX:MaxGCPauseMillis:設定最大垃圾收集停頓時間,可用把虛擬機器在GC停頓的時間控制在MaxGCPauseMillis範圍內,如果希望減少GC停頓時間可以將MaxGCPauseMillis設定的很小,但是會導致GC頻繁,從而增加了GC的總時間降低吞吐量。所以需要根據實際情況設定該值。

-Xx:GCTimeRatio:設定吞吐量大小,它是一個0到100之間的整數,預設情況下他的取值是99,那麼系統將花費不超過1/(1+n)的時間用於垃圾回收,也就是1/(1+99)=1%的時間。

另外還可以指定-XX:+UseAdaptiveSizePolicy開啟自適應模式,在這種模式下,新生代的大小、eden、from/to的比例,以及晉升老年代的物件年齡引數會被自動調整,以達到在堆大小、吞吐量和停頓時間之間的平衡點。

使用-XX:+UseParallelGC引數可以設定新生代使用這個並行回收器

老年代垃圾回收器

SerialOld 垃圾回收器

SerialOld是Serial回收器的老年代回收器版本,它同樣是一個單執行緒回收器。

用途

  • 一個是在JDK1.5及之前的版本中與Parallel Scavenge收集器搭配使用,
  • 另一個就是作為CMS收集器的後備預案,如果CMS出現Concurrent Mode Failure,則SerialOld將作為後備收集器。

使用演算法:標記 - 整理演算法

ParallelOldGC 回收器

老年代ParallelOldGC回收器也是一種多執行緒的回收器,和新生代的ParallelGC回收器一樣,也是一種關注吞吐量的回收器,他使用了標記壓縮演算法進行實現。

-XX:+UseParallelOldGc進行設定老年代使用該回收器

-XX:+ParallelGCThreads也可以設定垃圾收集時的執行緒數量。

CMS 回收器

CMS全稱為:Concurrent Mark Sweep意為併發標記清除,他使用的是標記清除法。主要關注系統停頓時間。

使用-XX:+UseConcMarkSweepGC進行設定老年代使用該回收器。

使用-XX:ConcGCThreads設定併發執行緒數量。

特點

CMS並不是獨佔的回收器,也就說CMS回收的過程中,應用程式仍然在不停的工作,又會有新的垃圾不斷的產生,所以在使用CMS的過程中應該確保應用程式的記憶體足夠可用。

CMS不會等到應用程式飽和的時候才去回收垃圾,而是在某一閥值的時候開始回收,回收閥值可用指定的引數進行配置:-XX:CMSInitiatingoccupancyFraction來指定,預設為68,也就是說當老年代的空間使用率達到68%的時候,會執行CMS回收。

如果記憶體使用率增長的很快,在CMS執行的過程中,已經出現了記憶體不足的情況,此時CMS回收就會失敗,虛擬機器將啟動老年代序列回收器;SerialOldGC進行垃圾回收,這會導致應用程式中斷,直到垃圾回收完成後才會正常工作。

這個過程GC的停頓時間可能較長,所以-XX:CMSInitiatingoccupancyFraction的設定要根據實際的情況。

之前我們在學習演算法的時候說過,標記清除法有個缺點就是存在記憶體碎片的問題,那麼CMS有個引數設定-XX:+UseCMSCompactAtFullCollecion可以使CMS回收完成之後進行一次碎片整理

-XX:CMSFullGCsBeforeCompaction引數可以設定進行多少次CMS回收之後,對記憶體進行一次壓縮

G1 回收器

篇幅太長,我們下篇文章講解!!!

image.png

IT 老哥

一個在大廠做高階Java開發的程式猿,一路自學走到今天,關注 老哥,我們一起來自學技術

相關文章