關於JVM的垃圾回收(GC) 這多是你想了解的

垃圾收集 (Garbage Collection) 機制是Java語言的一大優點特性, 爲充分榨取JVM性能, 避免系統因垃圾收集不及時致使的OOM (OutOfMemory, 內存溢出)問題, 或內存飽和出現沒法響應用戶請求的狀況, 就須要根據服務器配置及應用複雜度對GC策略進行優化, 以確保系統正常運行.java

1 JVM中Java對象的分類

JVM根據運行於其中的對象的生存時間, 將它們分爲3種, 並分別存放在JVM的不一樣內存區域中. 這種對象存放空間的管理方式叫作Generation管理方式.算法

(1) Young Generation (新生代, 又稱年輕代): 用於存放"早逝"對象(即瞬時對象), 通常的Java應用中, 80%的對象都是"朝生息滅"的, 好比在建立對象或調用方法時使用的臨時對象或局部變量.shell

(2) Tenured Generation (老年代): 用於存放"駐留"對象(即被引用較長時間的對象). 每每體現爲一個大型程序中的全局對象或長時間被使用的對象.服務器

(3) Perm Generation (永久代): 用於存放"永久"對象. 這些對象管理着運行於JVM中的類和方法.併發

2 JVM的GC類型及觸發條件

2.1 Young GC

又叫Minor GC(次收集), Young GC常常發生, 且其每次消耗的時間較短.oracle

—— 它只對Young Generation中的對象進行垃圾收集.app

觸發條件:jvm

在Young Generation(新生代)的Eden區的空間不足以容納新生成的對象時執行, 同時會將 Eden 區與 From Survivor 區中尚且存活的對象移動至空閒的 To Survivor 區中.分佈式

—— 程序運行過程當中, 始終有一個 Survivor 區是徹底處於空閒狀態的, 若是不是, 說明應用程序出現故障了.

2.2 Full GC

又叫Major GC(主收集), 是對整個Java Heap中的對象(不包括永久代/元空間)進行垃圾收集.

Full GC操做耗時久, 對系統的性能影響較大, 所以在 JVM 的調優中, 不少工做是針對 Full GC 的調優 —— 要儘量減小Full GC的頻率.

Full GC 是一種"昂貴"的垃圾收集方式, 它要對整個Heap進行垃圾收集, 並作必定的空間整理, 這會使Stop-The-World的時間變長.

Full GC的觸發條件:

(1) 年老代(Tenured)空間不足:

  • 經過Minor GC後進入老年代的對象的體積大於老年代的可用空間;
  • 由Eden塊、From Space塊向To Space複製存活對象時, 它們的體積大於To Space的大小, 系統就會把這些對象轉存到老年代, 而老年代的可用空間小於這些對象的體積.

(2) System.gc()方法被顯式調用, 系統建議執行Full GC, 但並不會當即執行 —— 很是影響程序性能, 建議禁止使用;

(3) 上一次GC以後Heap中各個區域空間的動態變化.

3 Java對象生成時的內存申請過程

(1) JVM會試圖爲相關Java對象在年輕代的Eden區中初始化一塊內存區域;

(2) 當Eden區空間足夠時, 內存申請結束. 不然執行下一步;

(3) JVM 試圖釋放在Eden區中全部不活躍的對象(即 出發Young GC), 釋放後若Eden空間仍然不足以放入新對象時, JVM會試圖將部分Eden區中活躍的對象遷移至Survivor區;

(4) Survivor區被用來做爲Eden區及老年代的中間交換區域, 當老年代空間足夠時, Survivor區中存活了必定次數的對象會被遷移到老年代;

(5) 當年老代空間不夠時, JVM會在老年代進行徹底的垃圾回收(Full GC);

(6) Full GC後, 若Survivor區及老年代仍然沒法存放從Eden區複製過來的對象, 則會致使JVM沒法在 Eden區爲新生成的對象申請內存, 即出現 "Out of Memory".

OOM(Out of Memory)異常通常主要有以下2種緣由:

(1) 老年代溢出, 表現爲: java.lang.OutOfMemoryError:Javaheapspace, 這是最多見的狀況, 產生的緣由多是: 設置的內存參數-Xmx太小或程序的內存泄露及使用不當問題.

(2) 持久代溢出,表現爲: java.lang.OutOfMemoryError:PermGenspace, 一般因爲持久代設置太小, 動態加載了大量 Java 類而致使溢出, 解決辦法惟有將參數 -XX:MaxPermSize 調大(通常256MB能知足絕大多數應用程序需求).

3 Oracle JDK中的垃圾收集器

3.1 串行收集器(Serial Collector)

只有一條GC線程, 運行時須要暫停用戶程序(Stop-The-World).

實現: Serial(用於新生代, 採用複製算法)、serial old(用於老年代, 採用標記-整理算法).

3.2 並行收集器(Parallel Collector)

有多條GC線程, 運行時也須要暫停用戶程序(Stop-The-World).

實現: ParNew(用於新生代, 採用複製算法)、Parallel Scavenge(用於新生代, 採用複製算法)、Parallel Old(用於老年代, 採用標記-整理算法).

3.3 併發收集器(Concurrent Collector)

有一條或多條GC線程, 且須要在部分階段暫停用戶程序(Stop-The-World), 部分階段與用戶程序併發執行.

實現: Concurrent Mark Sweep(CMS, 用於老年代, 採用標記-清除算法).

併發(concurrent)與並行(parallel)的比較:

(1) 併發就是兩個任務(A和B)須要獨立運行, 在任務A結束以前, 任務B開始執行 —— 即表面上多個任務同時執行.

(2) 並行, 類比串行, 是微觀概念, 即在每個時刻都有多個任務在同時執行, 形象點理解爲多管齊下, 串行可理解爲單列隊列, 同一時刻只能執行一個任務.

(3) 事實上, 並行是併發的一種實現方式, 還有一種併發的實現方式, 即咱們熟悉的時間片切換 —— 任務A執行一段時間, CPU再切換到任務B執行一段時間, 如此交替執行. 時間片切換在微觀上仍然是串行 —— 某一具體時刻只有一個任務在執行, 而在宏觀上, 即一段時間內, 有多個任務獲得了執行.

(4) 總結: 並行必須在多核多處理器或分佈式系統(本質仍是多核多處理器)中才能發生, 而單核處理器上只能發生時間片切換.

3.4 G1收集器(Garbage First GC)

G1垃圾回收器在 Oracle JDK 7 開始提供完整支持, 它是 server 型的 GC, 主要針對多核處理器和大內存的服務器, 可以以很高的機率 知足開發人員對停頓時間的要求, 同時還能保證高吞吐量.

(1) 與CMS收集器相比, G1收集器的優點:

① 基於標記-整理算法, 不會產生大量的內存碎片;
② 能夠更加精確地控制停頓時間, 在不犧牲吞吐量前提下, 實現低停頓垃圾回收.

(2) G1收集器的實現原理:

G1收集器可以避免全區域的垃圾收集, 它把堆內存劃分爲大小固定的幾個獨立區域, 並跟蹤這些區域的垃圾收集進度, 同時在後臺維護一個優先級列表, 每次根據所容許的收集時間, 優先回收垃圾最多的區域.
—— 區域劃分和優先級區域回收機制, 確保G1收集器能夠在有限的時間內得到最高的垃圾收集效率.

G1的長期目標是取代CMS (Concurrent Mark-Sweep Collector) 併發標記-清除收集器.

3.5 其餘概念說明

(1) 爲了更大程度地榨取機器性能, 新生代的收集器都使用了複製算法, 老年代的收集器都使用 標記-清除 或 標記-整理 算法. 關於各算法詳情, 請參閱: JVM內存管理———垃圾蒐集器參數精解.

(2) 在實際應用中, 須要對JVM的新生代、老年代分別選擇合適的垃圾收集器.

(3) 這裏新生代和老年代都分別有三種實現, 但因爲收集器的實現方式不一樣, 部分組合沒法一塊兒配合工做, 通過驗證, 這六種垃圾收集器只有六種可用組合.

4 GC的配置參數

4.1 參數名稱的說明

4.1.1 標準參數(-)

全部 JVM 都必須支持這些參數的功能, 並且向後兼容, 如:

-client : 設置 JVM 使用 client 模式, 特色是啓動速度比較快, 但運行時性能和內存管理效率不高. 一般用於客戶端應用程序或開發調試; 在32位環境下直接運行 Java 程序默認啓用該模式.

-server : 設置 JVM 使 server 模式, 特色是啓動速度比較慢, 但運行時性能和內存管理效率很高, 適用於生產環境; 在具備64位能力的JDK環境下默認啓用該模式.

4.1.2 非標準參數(-X)

各 JVM 廠商應該所有實現這些參數的功能, 但並不能保證這些廠商提供的 JVM 中都實現了這些功能, 且不保證向後兼容;

4.1.3 非穩定參數(-XX)

此類參數在各個 JVM 的實現中會有所不一樣, 未來也可能不被支持, 要慎重使用.

注意: 在"-XX:"後的參數若不須要賦值, 即只是用來配置開啓或關閉相應選項, 則須要有 "+" (開啓) 或 "-" (禁止) , 不然應用程序將在日誌文件 (如 Tomcat 的日誌文件 catalina.out ) 中拋出以下錯誤:

Missing +/- setting for VM option 'UseConcMarkSweepGC'. 
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

能夠看出, 這裏缺乏了 "+/-" 符號, 致使虛擬機啓動異常, 修改後便可正常啓動.

4.2 串行GC參數

-XX:+UseSerialGC 
# 使用 Serial & Serial Old 串行收集器(JDK 5之前的主要收集方式), 是client模式的默認設置.

4.3 並行GC參數(吞吐量優先)

-XX:+UseParNewGC 
# 使用ParNew & Serial Old收集器, 即對新生代使用並行收集, 提升收集效率(縮短Young GC的時間), 不推薦. 

-XX:+UseParallelGC 
# 使用Parallel Scavenge & Parallel Old並行收集器, 吞吐量優先, 會消耗更多內存, 是server模式的默認設置. 

-XX:+UseParallelOldGC 
# 對老年代使用Parallel Old並行收集器(JDK 6開始支持). 

-XX:ParallelGCThreads=20 
# 配置並行收集器的線程數, 即並行執行垃圾收集任務的線程個數. 
# 此值最好與CPU處理器的個數相同(默認即相同).

-XX:GCTimeRatio=49 
# 設置系統用做GC的時間比例, 如49, 則GC時間比爲 1/(1+49) = 2%, 即Java用2%的時間來作垃圾收集. 
# 若是此值設置過大, 即GC時間太少致使GC沒法完成, JVM會壓縮新生代的大小以適應此配置. 

-XX:MaxGCPauseMillis=100 
# 設置每次新生代垃圾收集的最長毫秒值, 若是時間久而新生代的大小不足以支撐到此時間, 
# JVM會自動調整新生代的大小以知足此值. 若仍然沒法知足, 則會調整GCTimeRatio. 

-XX:+UseAdaptiveSizePolicy 
# 使並行收集器自動設定Eden區的大小與相應的Survivor區的比例, 
# 以達到目標系統規定的最低響應時間或收集頻率等. 建議在使用並行收集器時始終開啓此選項.

4.4 併發GC參數(響應時間優先)

-XX:+UseConcMarkSweepGC 
# JDK 5開始提供支持, 以響應時間優先--即縮短Full GC的時間. 
# JVM會根據系統配置自行設置使用ParNewGC & CMS(Serial Old做爲替補)收集器. 
# 建議在Heap Size較大且Full GC時間較長, 對響應時間的需求大於對吞吐量的需求, 可以承受垃圾回收線程和應用線程共享CPU資源等狀況下使用. 

-XX:+UseCMSCompactAtFullCollection  
# CMS是不會移動內存的, 此參數設置在每次Full GC後對老年代空間進行壓縮整理, 會影響性能, 可是能減小內存碎片. 

-XX:CMSInitiatingOccupancyFraction=70 
# 觸發CMS收集器的內存佔用閾值, 默認爲90%: 當老年代內存空間使用率達到90%時, 就開始對老年代進行CMS併發垃圾收集. 
# 這個參數設置不當, 將發生promotion failed(晉升失敗). 

-XX:CMSFullGCsBeforeCompaction=10 
# 因爲併發收集器不對內存空間進行壓縮整理, 因此運行一段時間後會產生"碎片", 使得運行效率下降. 此配置項用來設置在幾回GC後觸發一次內存整理. 
# 此配置項即將被移除(JDK 8已不建議使用).

4.5 G1 GC參數

-XX:+UseG1GC 
# 使用G1收集器

-XX:MaxGCPauseMillis=200    
# 設置回收器的最大停頓毫秒值, 這是一個機率目標, JVM將盡最大努力去實現它. 

-XX:G1ReservePercent=15
# 設置堆的臨時上限, 以防止因堆擴大失敗而致使的異常. 默認值是10. 

-XX:G1HeapRegionSize=16
# 使用 G1 的 Java 堆細分爲大小相等的區域(Region), 此選項是設置單個區域的大小, 
# 默認值是基於堆大小的一種人體工效劃分, 最小值是1Mb, 最大值是32Mb.
# 人體工效: 根據平臺相關的默認選擇和根據需求動態垃圾回收的行爲統稱爲人體工效, 
# 人體工效的做用就是能夠經過少許的命令行選項就可讓JVM提供最合適的性能.

4.6 通用GC參數

-Xnoclassgc 
# 禁用類垃圾收集, 能提升性能. 

-XX:MaxHeapFreeRatio=70 
# GC事後堆的最大空閒空間比例, 避免過渡壓縮

-XX:MinHeapFreeRatio=40 
# GC事後堆的最小空閒空間比例, 避免過分膨脹

-XX:MaxTenuringThreshold=5 
# 晉升老年代的最大年齡, 默認爲15: 新生代對象在15次Minor GC後將被轉移至老年代. -- 必須在0-15之間. 
# 若是設置爲0, 等同於去掉了新生代的空間, 新生代對象將會越過Survivor區直接進入老年代, 很快就會佔滿老年代發生Full GC. 
# 同時, 這在老年代對象較多的應用中卻能夠提升效率. 
# 若是將此值調大, 則新生代對象會在Survivor區進行屢次複製, 即增長了對象在新生代的存活時間. 

-XX:PretenureSizeThreshold=10 
# 晉升老年代的對象的大小, 默認爲0. 好比設爲10M, 則超過10M的對象將越過Eden區, 直接存入老年代.

-XX:+UseThreadPriorities 
# 啓用本地線程優先級API, 使java.lang.Thread.setPriority()生效, 不啓用則無效

-XX:+DisableExplicitGC 
# 禁用寫在程序中的System.gc(), 即禁止開發人員調用gc()方法影響性能. 

-XX:+ExplicitGCInvokesConcurrent 
# 配置System.gc()能夠和應用程序一塊兒併發執行. 
# System.gc()用來回收不用的內存, 此方法只是"建議"JVM回收內存, 不能強制回收, 具體回收時機由JVM決定. 

-XX:TargetSurvivorRatio=90 
# 容許90%的Survivor區被佔用(JVM默認爲50%), 提升對於Survivor區的使用率

-XX:SoftRefLRUPolicyMSPerMB=1 
# Soft Reference(弱引用)在虛擬機中比在客戶機中存活的時間更長, 其清除頻率可用此命令來控制. 
# 此命令用來指定每MB堆空間中Soft Reference存活的秒數, 默認值爲1000毫秒: 對象的最後一個強引用被收集以後, 存活1秒鐘. 
# 這是個近似值: Soft Reference只會在垃圾收集時纔會被清除, 而垃圾收集並不老是發生. 可改成0, 客戶機中不使用就當即清除.

4.7 其餘說明

(1) 在內存調優中, 內存設置越大, 處理請求的效率也就越高, 但同時垃圾收集所須要的時間也就越長, 在垃圾收集期間的處理效率確定會受影響, 所以須要做出相應的權衡.

(2) 關於 CMS 收集器的 Promotion FailedConcurrent Mode Failure :

  • Promotion Failed, 晉升失敗: 發生Young GC時, Eden區存活的對象 和 Eden區的From塊中存活的對象, 二者的體積超過了Eden區中To塊的大小, Young GC的悲觀策略將使這些存活的對象都遷移到老年代, 而此時老年代的大小不足以容納這些對象, 從而發生promotion failed , 程序將暫停很長時間.
  • Concurrent Mode Failure, 併發修改失敗: 在執行GC的過程當中, 剛好有對象要晉升至老年代, 而此時老年代的空間不足, 從而形成Concurrent Mode Failure.
  • 這兩種狀況均可能觸發Full GC. 要注意不要一次性佔用太多的內存, 或對JVM的配置進行必定的優化.

  • CMSInitiatingOccupancyFraction的設置技巧: 參考: CMSInitiatingOccupancyFraction計算公式

    (Xmx-Xmn) * (1 - CMSInitiatingOccupancyFraction/100) >= (Xmn - Xmn/(SurvivorRatior+2))

    進而推斷出:

    CMSInitiatingOccupancyFraction <= ( (Xmx-Xmn) - (Xmn - Xmn/(SurvivorRatior+2) ) ) / (Xmx-Xmn) * 100

參考資料

JVM系列三:JVM參數設置、分析

G1(Garbage-First)垃圾回收器

JVM內存管理——垃圾蒐集器參數精解

版權聲明

做者: 馬瘦風

出處: 博客園 馬瘦風的博客

您的支持是對博主的極大鼓勵, 感謝您的閱讀.

本文版權歸博主全部, 歡迎轉載, 但請保留此段聲明, 並在文章頁面明顯位置給出原文連接, 不然博主保留追究相關人員法律責任的權利.

相關文章
相關標籤/搜索