java虛擬機(六)--垃圾收集器和內存分配策略

  目前沒有完美的收集器,不一樣的廠商、版本的虛擬機提供的垃圾收集器會有很大的差異,用戶根據本身應用特色和要求組合出各個年代所使用html

的收集器。基於jdk1.7Update14以後的虛擬機。java

官方文檔:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/toc.htmlweb

HotSpot的垃圾收集器

上圖是做用於新生代和老年代的收集器,連線表明能夠搭配使用。算法

分類:

一、串行收集器:Serial、Serial Old數組

  GC的時候須要暫停其餘線程的工做,啓動一個GC線程進行垃圾回收安全

二、並行收集器:Parallel Scavenge、Parallel Old,吞吐量優先服務器

  指的是多條垃圾收集線程並行工做,可是用戶線程仍是被暫停了。適合科學計算、後臺處理等弱交互場景多線程

三、併發收集器:CMS、G1,停頓時間優先併發

  指的是垃圾收集線程和用戶線程同時執行(不必定是並行,能夠是交替運行),不會出現Stop-the-world。適合對響應時間有要求的場景,如oracle

web應用。

停頓時間:

  垃圾收集器在GC的時候中斷應用程序的時間。-XX:MaxGCPauseMills

吞吐量:

  這裏的吞吐量爲GC時間和應用運行的時間的比值。-XX:GCTimeRatio=<n>,GC時間佔比爲1/(1+n)

新生代採用複製算法,而老年代採用標記-整理、標記-清除算法

一、Serial:串行

  採用複製算法的單線程收集器,進行GC的時候須要暫停其餘線程的工做(被稱爲Stop The World),直到它收集結束。Serial依然是運行在

Client模式下的默認新生代收集器,由於簡單、高效。用戶桌應用場景中,分配給虛擬機管理的內存通常來講不會很大,收集幾十兆甚至一兩百

兆的新生代停頓時間在幾十毫秒最多一百毫秒,只要不是頻繁發生,這點停頓是徹底能夠接受的。單個CPU環境下比較適合,Web應用不可能使用。

  -XX:+UseSerialGC  -XX:+UseSerialOldGC

二、ParNew

  ParNew收集器其實就是Serial收集器的多線程版本,除了使用多條線程進行垃圾收集外,其他行爲和Serial收集器徹底同樣,一些參數和回收策

略都相同。它是Server模式下的首選的新生代收集器,因爲除了Serial收集器外,目前只有它能與CMS收集器配合工做。它默認開啓的收集線程數與

CPU數量相同,在CPU數量很是多的狀況下,可使用-XX:ParallelGCThreads參數來限制垃圾收集的線程數。

三、Parallel Scavenge

  新生代,用複製算法,並行的多線程收集器,CMS等收集器的關注點是儘量縮短垃圾收集時用戶線程的停頓時間,而Parallel收集器的目標則

是打到一個可控制的吞吐量。另外,Parallel收集器是虛擬機運行在Server模式下的默認垃圾收集器。

  停頓時間短適合須要與用戶交互的程序,良好的響應速度能提高用戶體驗;高吞吐量則能夠高效率利用CPU時間,儘快完成運算任務,主要適合

後臺運算而不須要太多交互的任務。

經過如下命令查看當前運行的jvm啓用的是否爲Parallel垃圾回收器

[root@iZuf6fkfhthmdm1nwdg5isZ bin]# jinfo -flag UseParallelGC 24642
-XX:+UseParallelGC
[root@iZuf6fkfhthmdm1nwdg5isZ bin]# jinfo -flag UseParallelOldGC 24642
-XX:+UseParallelOldGC

Server模式:

  jvm自動判斷機制,根據當前系統CPU和內存等狀況,決定啓動Server模式,仍是Client模式。通常內存>2G,就是Server模式了

  -XX:+UseParallelGC  -XX:+UseParallelOldGC

-XX:ParallelGCThreads=<n>多少個線程,CPU>8 N=5/8,CPU<8 N=CPU

四、Serial Old

  Serial收集器的老年代版本,一樣是一個單線程收集器,使用「標記-整理算法」,這個收集器的主要意義也是在於給Client模式下的虛擬機使用。

五、Parallel Old

  Parallel收集器的老年代版本,使用多線程和「標記-整理」算法。這個收集器在JDK 1.6以後的出現,「吞吐量優先收集器」終於有了比較

名副其實的應用組合,在注重吞吐量以及CPU資源敏感的場合,均可以優先考慮Parallel收集器+Parallel Old收集器的組合。

六、CMS:Concurrent Mark Sweep 很是適用B/S系統

  以獲取最短GC停頓時間爲目標的老年代收集器。目前很大一部分Java應用集中在互聯網站或者B/S系統的服務端上,注重服務的響應速度,但願

統停頓時間最短,以給用戶帶來較好的體驗,CMS收集器就非常符合這類應用的需求。CMS收集器基於「標記-清除」算法實現的。

  -XX:+UserConcMarkSweepGC  -XX:+UseParNewGC

咱們在Catalina.xml中經過JAVA_OPTS設置:-XX:+UserConcMarkSweepGC,而後查看

[root@iZuf6fkfhthmdm1nwdg5isZ bin]# jinfo -flag UseConcMarkSweepGC 29720
-XX:+UseConcMarkSweepGC
[root@iZuf6fkfhthmdm1nwdg5isZ bin]# jinfo -flag UseParNewGC 29720
-XX:+UseParNewGC

運行過程分爲四步:

  初始標記:會有GC停頓,只是標記GC Root,速度很快,會發生Stop-The-Word

  併發標記:進行GC Roots Tracing的過程

  併發預清理

  從新標記:會有GC停頓,修正併發標記期間標記產生變更的那部分對象的標記,會發生Stop-The-Word

  併發清除:

  併發重置:

  併發標記、併發清除是耗時最長的可是能夠與用戶線程同時工做

缺點:

  一、CPU敏感,佔用CPU資源,應用程序吞吐量降低

  二、併發清理階段用戶線程還在運行,就會產生浮動垃圾,因爲標記過了,只能下次GC才能清理

  三、採用"標記-清除"算法就會產生大量空間碎片,大對象分配很麻煩,可能提早Full GC

相關參數:

-XX:ConcGCThreads:與應用程序併發執行的GC線程數,不是Stop-the-world的線程數
-XX:+UseCMSCompactAtFullCollection:Full GC以後進行壓縮,由於會產生碎片
-XX:CMSFullGCsBeforeCompaction:發生多少次Full GC壓縮一次
-XX:CMSInitiatingOccupancyFraction:Old區內存使用達到多少,纔會觸發Full GC,默認92%
-XX:+UseCMSInitiatingOccupancyOnly:是否能夠冬天調整
-XX:CMScavengeBeforeRemark:Full GC以前先作Young GC,通常開啓
-XX:+CMSClassUnloadingEnabled:啓用回收Perm區

七、G1(Garbage-First):兼顧吞吐量和停頓時間的GC實現,是JDK9之後的默認GC選項,新生代和老生代的收集器

  JDK 7 Update 4後開始進入商用。使用G1收集器時,Java堆的內存佈局就與其餘收集器有很大差異,它將整個Java堆分爲多個大小相等的獨立

區域(Region),雖然還保留有新生代和老年代的概念,但新生代和老年代再也不是物理隔離的了,它們都是一部分Region的集合。G1收集器跟蹤各

個Region裏面的垃圾堆積的價值大小,在後臺維護一個優先列表,每次根據容許的收集時間,優先回收價值最大的Region(這也是Garbage-First

名稱的由來)。這種使用Region劃份內存空間以及有優先級的區域回收方式,保證了G1收集器在有限的時間內能夠獲取儘量高的收集效率。

描述:

Region:

  上圖爲G1收集器的樣式,每個小塊就是一個Region,一部分Region組成Young區,一部分組成Old區,Young和Old只是邏輯的分佈,其中標明

H的Region是專門用來保存大對象的,超過Region大小的一半,就會把對象存放到H Region中。

SATB:

  Snapshot-At-The-Begining,經過GC-Root獲得,GC開始時存活的快照。

RSet:

  記錄其餘Region中對象引用自身Region中對象的關係,屬於points-into結構(誰引用了個人對象)。

Young GC:

  一、新對象進入Eden區

  二、存活對象拷貝到Survivor區

  三、存活時間到達年齡閥值,晉升到Old區

MixedGC:

  一、不是Full GC,而是回收全部的Young和部分Old

  二、這個過程會有global concurrent marking

global concurrent marking:

  一、初始標記:會有GC停頓,只是標記GC Root,速度很快,會發生Stop-The-Word

  二、併發標記:標記存活的Region

  三、標記存活的對象

  四、從新標記:會有GC停頓,修正併發標記期間標記產生變更的那部分對象的標記,會發生Stop-The-Word

  五、併發清除:部分發生Stop-The-Word

Mixed GC發生的時機:

InitiatingHeapOccupancyPercent:

  堆佔有率達到這個數值則觸發global concurrent marking,默認45%

G1HeapWastePercent:

  在global concurrent marking結束以後,能夠知道區中有多少空間要被回收,在每次YGC以後和再次發生Mixed GC以前,會檢查垃圾佔比是

否達到這個參數,若是達到了,下次纔會發生Mixed GC。

G1MixedGCLiveThresholdPercent:

  Old區Region被回收的時候,存貨對象的佔比

G1MixedGCCountPercent:

  一次global concurrent marking以後,最多執行Mixed GC的次數

能夠參考:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/g1_gc_tuning.html#g1_gc_tuning

官方文檔把全部的內容寫得都很詳細,英語很差的話就翻譯過來看,儘可能原文看,還能學習英語

G1最佳實踐:

一、年輕代大小:

  避免使用-Xmn、-XX:NewRatio等顯式設置Young區大小,會覆蓋暫停時間目標

二、暫停時間目標:

  暫停時間不要太過嚴苛,吞吐量目標是90%應用程序時間和10%的垃圾回收時間,太嚴苛會影響到吞吐量

須要切換到G1垃圾回收器的條件:

  一、堆的50%被存貨對象佔用

  二、對象分配和晉升的速度變化很大

  三、垃圾回收時間很長,超過1S

PS:-XX:+UseG1GC,jdk8以後,推薦使用G1垃圾回收器,吞吐量比較高,目標是在於大內存6G+,停頓時間小於0.5s

與其餘GC收集器相比,G1的特色:

1).併發與並行

  充分利用多CPU、多核環境下的硬件優點,來縮短Stop-The-World停頓時間,G1能夠經過併發讓java程序繼續執行

2).分帶收集

  經過不一樣的方式處理新建立的對象和存貨了一段時間、熬過屢次GC的就對象來獲取更好的收集效果

3).空間整合

  G1從總體來看基於「標記-整理」算法實現的收集器,從局部(兩個Region之間)看基於「複製」算法實現

並且這兩種算法不會產生內存空間碎片,收集後能提供規整的可用內存

4).可預測的停頓

  G1處理追求低停頓,還能創建可預測的停頓時間模型,讓使用者明確指定在一個長度爲Mms的時間片斷內,消耗在GC的時間不超過Nms,幾乎已經

是實時Java的垃圾收集器的特徵了。

  Region之間對象引用以及其餘收集器中的新聲代和老年代之間的對象引用,虛擬機都是使用Rememberd Set來避免全堆掃描。

若是不計算維護Rememberd Set的操做,收集步驟以下:

  1).初始標記

  2).併發標記

  3).最終標記

  4).篩選回收

如何選擇垃圾回收器?

一、優先調整堆的大小讓服務器本身選擇

二、若是內存小於100M,使用串行收集器

三、若是是單核,而且沒有停頓時間的要求,選擇串行或者服務器本身選擇

四、若是容許停頓時間超過1S,選擇並行或者服務器本身選擇

五、若是響應時間很重要,而且不能超過1S,使用併發收集器

可參考官方文檔:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/collectors.html

想要對這部份內容很瞭解,官方文檔必定要多讀幾遍。。。

Full GC觸發條件:

(1)調用System.gc時,系統建議執行Full GC,可是不是必然執行

(2)老年代空間不足

(3)方法區空間不足

(4)經過Minor GC後進入老年代的平均大小大於老年代的可用內存

(5)由Eden區、From Space區向To Space區複製時,對象大小大於To Space可用內存,則把該對象轉存到老年代,且老年代的可用內存小於該對

象大小

內存分配與回收策略:

內存分配的規則取決於使用的是哪種垃圾回收收集器組合,還有虛擬機中與內存相關參數的配置

一、對象優先Eden space分配,若是Eden Space沒有足夠的空間,會發生一次Minor GC

二、大對象(很長的字符串、數組)直接分配獲得老年代,經過參數-XX:PretenureSizeThreshold=3145728(3M),只要大於這個值,對象直接分

配到老年代,防止新生代進行大量內存複製

三、長時間存活的對象,會轉到老年代,每一個對象有個年齡計數器,在survivor每次通過一次Minor GC,就會加一,默認15的時候,會分到老年代

參數爲:-XX:MaxTenuringThreshold

四、第三條不是絕對的,VM會動態判斷

  -XX:TargetSurvivorRatio:若是進行進行一次GC存活的對象超過這個參數,會計算相同年齡全部對象大小的綜合大於Survivor空間的一半,

年齡大於或等於該年齡的對象就能夠直接進入老年代,無須等到MaxTenuringThreshold要求的年齡

空間擔保失敗:

  發生Minor GC以前,虛擬機會檢查老年代最大可用的連續空間是否大於新生代全部對象總空間,若是成立,能夠確保Minor GC是安全的,else

檢查HandlerPromotionFailure設置值是否容許擔保失敗。if許,繼續檢查老年代最大可用的連續空間是否大於歷次晉升到老年代對象的平均大小

,true,查實進行一次Minor GC,儘管有風險,若是小於,或者參數設置不容許,改成進行一次Full GC可是HandlerPromotionFailure在jdk 6

Update 24沒有做用了,jdk規則變成只要老年代的連續空間大於新生代對象總大小或者歷次晉升的平均大小就會進行Minor GC,不然進行Full GC

相關文章
相關標籤/搜索