JVM垃圾回收器詳解




說明

針對jdk7

關於垃圾回收

可以查看我之前的博文:
JVM內存管理-垃圾回收與內存分配

垃圾收集器分類

  • 按線程數分

可以分爲串行垃圾回收器和並行垃圾回收器。串行垃圾回收器一次只使用一個線程進行垃圾回收;並行垃圾回收器一次將開啓多個線程同時進行垃圾回收。在並行能力較強的 CPU 上,使用並行垃圾回收器可以縮短 GC 的停頓時間。

  • 按照工作模式分

可以分爲併發式垃圾回收器和獨佔式垃圾回收器。併發式垃圾回收器與應用程序線程交替工作,以儘可能減少應用程序的停頓時間;獨佔式垃圾回收器 (Stop the world) 一旦運行,就停止應用程序中的其他所有線程,直到垃圾回收過程完全結束

  • 按碎片處理方式

可分爲壓縮式垃圾回收器和非壓縮式垃圾回收器。壓縮式垃圾回收器會在回收完成後,對存活對象進行壓縮整理,消除回收後的碎片;非壓縮式的垃圾回收器不進行這步操作。

  • 按工作的內存區間

可分爲新生代垃圾回收器和老年代垃圾回收器

垃圾收集器的指標

  • 吞吐量

指在應用程序的生命週期內,應用程序所花費的時間和系統總運行時間的比值。系統總運行時間=應用程序耗時+GC 耗時。如果系統運行了 100min,GC 耗時 1min,那麼系統的吞吐量就是 (100-1)/100=99%。
垃圾回收器負載:和吞吐量相反,垃圾回收器負載指來記回收器耗時與系統運行總時間的比值。

  • 停頓時間

指垃圾回收器正在運行時,應用程序的暫停時間。對於獨佔回收器而言,停頓時間可能會比較長。使用併發的回收器時,由於垃圾回收器和應用程序交替運行,程序的停頓時間會變短,但是,由於其效率很可能不如獨佔垃圾回收器,故系統的吞吐量可能會較低。

  • 垃圾回收頻率

指垃圾回收器多長時間會運行一次。一般來說,對於固定的應用而言,垃圾回收器的頻率應該是越低越好。通常增大堆空間可以有效降低垃圾回收發生的頻率,但是可能會增加回收產生的停頓時間。

  • 反應時間

指當一個對象被稱爲垃圾後多長時間內,它所佔據的內存空間會被釋放。

  • 堆分配

不同的垃圾回收器對堆內存的分配方式可能是不同的。一個良好的垃圾收集器應該有一個合理的堆內存區間劃分。

垃圾收集器介紹

新生代串行收集器

串行收集器主要有兩個特點:第一,它僅僅使用單線程進行垃圾回收;第二,它獨佔式的垃圾回收。

在串行收集器進行垃圾回收時,Java 應用程序中的線程都需要暫停,等待垃圾回收的完成,這樣給用戶體驗造成較差效果。雖然如此,串行收集器卻是一個成熟、經過長時間生產環境考驗的極爲高效的收集器。新生代串行處理器使用複製算法,實現相對簡單,邏輯處理特別高效,且沒有線程切換的開銷。在諸如單 CPU 處理器或者較小的應用內存等硬件平臺不是特別優越的場合,它的性能表現可以超過並行回收器和併發回收器。在 HotSpot 虛擬機中,使用-XX:+UseSerialGC 參數可以指定使用新生代串行收集器和老年代串行收集器。當 JVM 在 Client 模式下運行時,它是默認的垃圾收集器。

老年代串行收集器

老年代串行收集器使用的是標記-壓縮算法。和新生代串行收集器一樣,它也是一個串行的、獨佔式的垃圾回收器。由於老年代垃圾回收通常會使用比新生代垃圾回收更長的時間,因此,在堆空間較大的應用程序中,一旦老年代串行收集器啓動,應用程序很可能會因此停頓幾秒甚至更長時間。雖然如此,老年代串行回收器可以和多種新生代回收器配合使用,同時它也可以作爲 CMS 回收器的備用回收器。若要啓用老年代串行回收器,可以嘗試使用以下參數:-XX:+UseSerialGC: 新生代、老年代都使用串行回收器。

如果使用-XX:+UseParNewGC 參數設置,表示新生代使用並行收集器,老年代使用串行收集器

如果使用-XX:+UseParallelGC 參數設置,表示新生代和老年代均使用並行回收收集器

【聲明:轉載請註明出處
獨立:http://wangnan.tech
簡書:http://www.jianshu.com/u/244399b1d776
CSDN:http://blog.csdn.net/wangnan9279

並行收集器

並行收集器是工作在新生代的垃圾收集器,它只簡單地將串行回收器多線程化。它的回收策略、算法以及參數和串行回收器一樣。

並行回收器也是獨佔式的回收器,在收集過程中,應用程序會全部暫停。但由於並行回收器使用多線程進行垃圾回收,因此,在併發能力比較強的 CPU 上,它產生的停頓時間要短於串行回收器,而在單 CPU 或者併發能力較弱的系統中,並行回收器的效果不會比串行回收器好,由於多線程的壓力,它的實際表現很可能比串行回收器差。

開啓並行回收器可以使用參數-XX:+UseParNewGC,該參數設置新生代使用並行收集器,老年代使用串行收集器。

設置參數-XX:+UseConcMarkSweepGC 可以要求新生代使用並行收集器,老年代使用 CMS。

並行收集器工作時的線程數量可以使用-XX:ParallelGCThreads 參數指定。一般,最好與 CPU 數量相當,避免過多的線程數影響垃圾收集性能

新生代並行回收 (Parallel Scavenge) 收集器

新生代並行回收收集器也是使用複製算法的收集器。從表面上看,它和並行收集器一樣都是多線程、獨佔式的收集器。但是,並行回收收集器有一個重要的特點:它非常關注系統的吞吐量。

新生代並行回收收集器可以使用以下參數啓用:

-XX:+UseParallelGC:新生代使用並行回收收集器,老年代使用串行收集器。

-XX:+UseParallelOldGC:新生代和老年代都是用並行回收收集器。

新生代並行回收收集器可以使用以下參數啓用:

-XX:+MaxGCPauseMills:設置最大垃圾收集停頓時間,它的值是一個大於 0 的整數。收集器在工作時會調整 Java 堆大小或者其他一些參數,儘可能地把停頓時間控制在 MaxGCPauseMills 以內。如果希望減少停頓時間,而把這個值設置得很小,爲了達到預期的停頓時間,JVM 可能會使用一個較小的堆 (一個小堆比一個大堆回收快),而這將導致垃圾回收變得很頻繁,從而增加了垃圾回收總時間,降低了吞吐量。

-XX:+GCTimeRatio:設置吞吐量大小,它的值是一個 0-100 之間的整數。假設 GCTimeRatio 的值爲 n,那麼系統將花費不超過 1/(1+n) 的時間用於垃圾收集。比如 GCTimeRatio 等於 19,則系統用於垃圾收集的時間不超過 1/(1+19)=5%。默認情況下,它的取值是 99,即不超過 1%的時間用於垃圾收集。

除此之外,並行回收收集器與並行收集器另一個不同之處在於,它支持一種自適應的 GC 調節策略,使用-XX:+UseAdaptiveSizePolicy 可以打開自適應 GC 策略。在這種模式下,新生代的大小、eden 和 survivor 的比例、晉升老年代的對象年齡等參數會被自動調整,以達到在堆大小、吞吐量和停頓時間之間的平衡點。在手工調優比較困難的場合,可以直接使用這種自適應的方式,僅指定虛擬機的最大堆、目標的吞吐量 (GCTimeRatio) 和停頓時間 (MaxGCPauseMills),讓虛擬機自己完成調優工作

老年代並行回收收集器

老年代的並行回收收集器也是一種多線程併發的收集器。和新生代並行回收收集器一樣,它也是一種關注吞吐量的收集器。老年代並行回收收集器使用標記-壓縮算法,JDK1.6 之後開始啓用。

使用-XX:+UseParallelOldGC 可以在新生代和老生代都使用並行回收收集器,這是一對非常關注吞吐量的垃圾收集器組合,在對吞吐量敏感的系統中,可以考慮使用。參數-XX:ParallelGCThreads 也可以用於設置垃圾回收時的線程數量。

CMS 收集器

與並行回收收集器不同,CMS 收集器主要關注於系統停頓時間。CMS 是 Concurrent Mark Sweep 的縮寫,意爲併發標記清除,從名稱上可以得知,它使用的是標記-清除算法,同時它又是一個使用多線程併發回收的垃圾收集器。

CMS 工作時,主要步驟有:初始標記、併發標記、重新標記、併發清除和併發重置。其中初始標記和重新標記是獨佔系統資源的,而併發標記、併發清除和併發重置是可以和用戶線程一起執行的。因此,從整體上來說,CMS 收集不是獨佔式的,它可以在應用程序運行過程中進行垃圾回收。

根據標記-清除算法,初始標記、併發標記和重新標記都是爲了標記出需要回收的對象。併發清理則是在標記完成後,正式回收垃圾對象;併發重置是指在垃圾回收完成後,重新初始化 CMS 數據結構和數據,爲下一次垃圾回收做好準備。併發標記、併發清理和併發重置都是可以和應用程序線程一起執行的。

CMS 收集器在其主要的工作階段雖然沒有暴力地徹底暫停應用程序線程,但是由於它和應用程序線程併發執行,相互搶佔 CPU,所以在 CMS 執行期內對應用程序吞吐量造成一定影響。CMS 默認啓動的線程數是 (ParallelGCThreads+3)/4),ParallelGCThreads 是新生代並行收集器的線程數,也可以通過-XX:ParallelCMSThreads 參數手工設定 CMS 的線程數量。當 CPU 資源比較緊張時,受到 CMS 收集器線程的影響,應用程序的性能在垃圾回收階段可能會非常糟糕。

由於 CMS 收集器不是獨佔式的回收器,在 CMS 回收過程中,應用程序仍然在不停地工作。在應用程序工作過程中,又會不斷地產生垃圾。這些新生成的垃圾在當前 CMS 回收過程中是無法清除的。同時,因爲應用程序沒有中斷,所以在 CMS 回收過程中,還應該確保應用程序有足夠的內存可用。因此,CMS 收集器不會等待堆內存飽和時才進行垃圾回收,而是當前堆內存使用率達到某一閾值時,便開始進行回收,以確保應用程序在 CMS 工作過程中依然有足夠的空間支持應用程序運行。

這個回收閾值可以使用-XX:CMSInitiatingOccupancyFraction 來指定,默認是 68。即當老年代的空間使用率達到 68%時,會執行一次 CMS 回收。如果應用程序的內存使用率增長很快,在 CMS 的執行過程中,已經出現了內存不足的情況,此時,CMS 回收將會失敗,JVM 將啓動老年代串行收集器進行垃圾回收。如果這樣,應用程序將完全中斷,直到垃圾收集完成,這時,應用程序的停頓時間可能很長。因此,根據應用程序的特點,可以對-XX:CMSInitiatingOccupancyFraction 進行調優。如果內存增長緩慢,則可以設置一個稍大的值,大的閾值可以有效降低 CMS 的觸發頻率,減少老年代回收的次數可以較爲明顯地改善應用程序性能。反之,如果應用程序內存使用率增長很快,則應該降低這個閾值,以避免頻繁觸發老年代串行收集器。

標記-清除算法將會造成大量內存碎片,離散的可用空間無法分配較大的對象。在這種情況下,即使堆內存仍然有較大的剩餘空間,也可能會被迫進行一次垃圾回收,以換取一塊可用的連續內存,這種現象對系統性能是相當不利的,爲了解決這個問題,CMS 收集器還提供了幾個用於內存壓縮整理的算法。

-XX:+UseCMSCompactAtFullCollection 參數可以使 CMS 在垃圾收集完成後,進行一次內存碎片整理。內存碎片的整理並不是併發進行的。-XX:CMSFullGCsBeforeCompaction 參數可以用於設定進行多少次 CMS 回收後,進行一次內存壓縮。

-XX:CMSInitiatingOccupancyFraction 設置爲 100,同時設置-XX:+UseCMSCompactAtFullCollection 和-XX:CMSFullGCsBeforeCompaction,日誌輸出如下:

關於CMS收集器,深入瞭解閱讀下面的博文
link

G1 收集器 (Garbage First)

G1 收集器的目標是作爲一款服務器的垃圾收集器,因此,它在吞吐量和停頓控制上,預期要優於 CMS 收集器。

與 CMS 收集器相比,G1 收集器是基於標記-壓縮算法的。因此,它不會產生空間碎片,也沒有必要在收集完成後,進行一次獨佔式的碎片整理工作。G1 收集器還可以進行非常精確的停頓控制。它可以讓開發人員指定當停頓時長爲 M 時,垃圾回收時間不超過 N。使用參數-XX:+UnlockExperimentalVMOptions –XX:+UseG1GC 來啓用 G1 回收器,設置 G1 回收器的目標停頓時間:-XX:MaxGCPauseMills=20,-XX:GCPauseIntervalMills=200。

關於G1收集器,深入瞭解閱讀下面的博文
link
link

GC 相關參數總結

  1. 與串行回收器相關的參數
    -XX:+UseSerialGC:在新生代和老年代使用串行回收器。
    -XX:+SurvivorRatio:設置 eden 區大小和 survivor 區大小的比例。
    -XX:+PretenureSizeThreshold:設置大對象直接進入老年代的閾值。當對象的大小超過這個值時,將直接在老年代分配。
    -XX:MaxTenuringThreshold:設置對象進入老年代的年齡的最大值。每一次 Minor GC 後,對象年齡就加 1。任何大於這個年齡的對象,一定會進入老年代。
  2. 與並行 GC 相關的參數
    -XX:+UseParNewGC: 在新生代使用並行收集器。
    -XX:+UseParallelOldGC: 老年代使用並行回收收集器。
    -XX:ParallelGCThreads:設置用於垃圾回收的線程數。通常情況下可以和 CPU 數量相等。但在 CPU 數量比較多的情況下,設置相對較小的數值也是合理的。
    -XX:MaxGCPauseMills:設置最大垃圾收集停頓時間。它的值是一個大於 0 的整數。收集器在工作時,會調整 Java 堆大小或者其他一些參數,儘可能地把停頓時間控制在 MaxGCPauseMills 以內。
    -XX:GCTimeRatio:設置吞吐量大小,它的值是一個 0-100 之間的整數。假設 GCTimeRatio 的值爲 n,那麼系統將花費不超過 1/(1+n) 的時間用於垃圾收集。
    -XX:+UseAdaptiveSizePolicy:打開自適應 GC 策略。在這種模式下,新生代的大小,eden 和 survivor 的比例、晉升老年代的對象年齡等參數會被自動調整,以達到在堆大小、吞吐量和停頓時間之間的平衡點。
  3. 與 CMS 回收器相關的參數
    -XX:+UseConcMarkSweepGC: 新生代使用並行收集器,老年代使用 CMS+串行收集器。
    -XX:+ParallelCMSThreads: 設定 CMS 的線程數量。
    -XX:+CMSInitiatingOccupancyFraction:設置 CMS 收集器在老年代空間被使用多少後觸發,默認爲 68%。
    -XX:+UseFullGCsBeforeCompaction:設定進行多少次 CMS 垃圾回收後,進行一次內存壓縮。
    -XX:+CMSClassUnloadingEnabled:允許對類元數據進行回收。
    -XX:+CMSParallelRemarkEndable:啓用並行重標記。
    -XX:CMSInitatingPermOccupancyFraction:當永久區佔用率達到這一百分比後,啓動 CMS 回收 (前提是-XX:+CMSClassUnloadingEnabled 激活了)。
    -XX:UseCMSInitatingOccupancyOnly:表示只在到達閾值的時候,才進行 CMS 回收。
    -XX:+CMSIncrementalMode:使用增量模式,比較適合單 CPU。
  4. 與 G1 回收器相關的參數
    -XX:+UseG1GC:使用 G1 回收器。
    -XX:+UnlockExperimentalVMOptions:允許使用實驗性參數。
    -XX:+MaxGCPauseMills:設置最大垃圾收集停頓時間。
    -XX:+GCPauseIntervalMills:設置停頓間隔時間。
  5. 其他參數
    -XX:+DisableExplicitGC: 禁用顯示 GC。

(本文完)
參考:
- https://www.ibm.com/developerworks/cn/java/j-lo-JVMGarbageCollection/
- https://github.com/pzxwhc/MineKnowContainer/issues/89
- https://github.com/pzxwhc/MineKnowContainer/issues/90
- http://ifeve.com/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3g1%E5%9E%83%E5%9C%BE%E6%94%B6%E9%9B%86%E5%99%A8/
- http://chenchendefeng.iteye.com/blog/455883
- http://www.jianshu.com/p/50d5c88b272d

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章