JVM學習垃圾收集器

1.作用

  用來替代手工內存管理,減輕程序員的負擔,減少出錯的概率。
垃圾收集器要處理的基本問題是:

  • 那些對象需要回收?
  • 何時回收這些對象?
  • 如何回收這些對象?

2.垃圾回收算法與思想

1.引用計數法(Reference Counting)

  對於一個對象A,只要有任何一個對象引用了A,則A的引用計數器就加1,當引用失效時,引用計數器就減一。只要對象A的引用計數器的值爲0,則對象A就不可能再被使用。
  引用計數器無法處理循環引用的情況,因此,Java的垃圾回收器中,沒有使用這種算法。

2. 標記-清除算法(Mark-Sweep)

說明:標記-清除算法是現代垃圾回收算法的思想基礎。將垃圾回收分爲兩個階段:標記階段和清除階段。在標記階段,首先通過根節點,標記所有從根節點開始的可達對象。因此,未被標記的對象就是未被引用的垃圾對象。然後在清除階段,清除所有未被標記的對象。
缺點:標記-清除算法可能產生的最大問題就是空間碎片。垃圾回收後的空間是不連續的。在對象的堆空間分配過程中,尤其是大對象的內存分配,不連續的內存空間的工作效率要低於連續的空間,這也是該算法的最大缺點。

3.複製算法(Copying)

  • 說明:與標記-清除算法相比,複製算法是一種相對高效的回收方法。將原有的內存空間分爲兩塊,每次只使用其中一塊,在垃圾回收時,將正在使用的內存中的存活對象複製到未使用的內存塊中,之後,清除正在使用的內存塊中的所有對象,交換兩個內存的角色,完成垃圾回收。
  • 優點:如果系統中的垃圾對象很對,複製算法需要複製的存活對象數量並不會太大。因此,在真正需要垃圾回收的時刻,複製算法的效率是很高的。又由於對象是在垃圾回收過程中統一被複制到新的內存空間中,因此,可確保回收後的內存空間是沒有碎片的。
  • 缺點:複製算法的代價缺點是將系統內存摺半,所以單純的複製算法也很難讓人接受。
  • 注意:複製算法比較適用於新生代,因爲在新生代,垃圾對象通常會多餘存活對象,複製算法的效果會比較好。

4.標記-壓縮算法(Mark-Compact)

  • 說明:標記-壓縮算法是一種老年代的回收算法,它在標記-清除算法的基礎上做了一些優化。和標記-清除算法一樣,標記-壓縮算法也首先需要從根節點開始,對所有可達對象做一次標記。但之後,它並不簡單地清理未標記的對象,而是將所有的存活對象壓縮到內存的一端。之後,清理邊界外所有的空間。
  • 優點:既避免了碎片的產生,又不需要兩塊相同的內存空間,因此其性價比較高。
  • 缺點:
  • 注意:

5.增量算法(Incremental Collecting)

  • 說明:對大部分垃圾回收算法而言,在垃圾回收過程中,應用軟件將處於一種Stop the World的狀態。在Stop the World狀態下,應用程序所有的線程都會掛起,暫停一切正常的工作,等待垃圾回收的完成。如果垃圾回收時間很長,將嚴重影響用戶體驗或者系統的穩定性。
    增量算法的基本思想是,如果一次性將所有的垃圾進行處理,需要造成系統長時間的停頓,那麼就可以讓垃圾收集線程和應用程序線程交替執行。每次,垃圾手機現場只收集一小片區域的內存空阿金,接着切換到應用程序線程。以此反覆,直到垃圾收集完成。
  • 優點:由於在垃圾回收的過程中,間斷性地還執行了應用程序代碼,所以能減少系統的停頓時間。
  • 缺點:因爲線程切換和上下文轉換的消耗,會使得垃圾回收的總體成本上升,造成系統吞吐量的下降。

6.分代(Generational Collecting)

  • 說明:根據垃圾回收對象的特性,使用合適的算法回收,纔是明智的選擇。將內存區間根據對象的特點分成幾塊,根據每塊內存區間的特點,使用不同的回收算法,以提高垃圾回收的效率。
    如:

3.垃圾收集器的類型

4.評價GC策略的指標

  • 吞吐量:指在應用程序的什麼週期內,應用程序所花費的時間和系統的總運行時間的比值。系統總運行時間=應用程序耗時+GC耗時。
  • 垃圾回收器負載:和吞吐量相反,垃圾回收期負載指垃圾回收器耗時與系統運行總時間的比值。
  • 停頓時間:指垃圾回收器正在運行時,應用程序的暫停時間。對於獨佔回收器而言,停頓時間可能會比較長。使用併發的回收器時,由於垃圾回收和應用程序交替運行,程序的停頓時間會變短,但是由於其效率很可能不如獨佔垃圾回收器,股系統的吞吐量可能會較低。
  • 垃圾回收頻率:指垃圾回收器多長時間會運行一次。一般來說,對於固定的應用而言,垃圾回收器的評率應該是越低越好。通常增大堆空間可以有效降低垃圾回收發生的評率,但是可能會增加回收產生的停頓時間。
  • 反應時間:指當一個對象稱爲垃圾後,多長商檢內,它所佔據的內存空間會被釋放。
  • 堆分配:不同的垃圾回收器對堆內存的分配方式可能是不同的。一個良好的垃圾收集器應該有一個合理的堆內存區間劃分。

5.新生代串行收集器

  • 說明:串行收集器是所有垃圾收集器中最古老的一種,也是JDK種最基本的垃圾收集器之一。串行收集器主要有兩個特點:第一,它僅僅使用單線程進行垃圾回收;第二,它是獨佔式的垃圾回收。
  • 使用場景:在諸如單CPU處理器或者較小的應用內存等硬件平臺不是特別優越的場合,它的性能表現可以超過並行回收器和併發回收器。
  • 使用方式:-XX:+UseSerialGC

6.老年代串行收集器

  • 說明:老年代串行收集器使用的是標記-壓縮算法。和新生代串行收集器一樣,它也是一個串行的、獨佔式的垃圾回收器。由於老年代垃圾回收通常會使用比新生代垃圾回收更長的時間。因此,在堆空間較大的應用程序中,一旦老年代串行收集器啓動,應用程序很可能會因此停頓幾秒甚至更長時間。
  • 使用方式: -XX:+UseSerialGC:新生代、老年代都使用串行回收器。
    -XX:+UseParNewGC:新生代使用並行收集器,老年代使用串行收集器。
    -XX:+UseParallelGC:新生代使用並行回收收集器,老年代使用串行收集器。

7.並行收集器

  • 說明:並行收集器是工作在新生代的垃圾收集器,它只是簡單地將串行回收器多線程化。並行回收器也是獨佔式的回收器,在收集過程中,應用程序會全部暫停。但由於並行回收器使用多線程進行垃圾回收,因此,在併發能力比較強的CPU上,它產生的停頓時間要短於串行回收器,而在單CPU或者併發能力較弱的系統中,並行回收器的效果不會比串行回收器好。由於多線程的壓力,它的實際表現很可能比串行回收器差。
  • 使用方式:-XX:+UseParNewGC:新生代使用並行收集器,老年代使用串行收集器。
    -XX:+UseConcMarkSweepGC:新生代使用並行收集器,老年代使用CMS。
    -XX:ParallelGCThreads:一般最好與CPU數量相當,避免過多的線程數,影響垃圾收集性能。(默認情況下,CPU數量小於8個時,該值等於CPU的數量;當CPU數量大於8個時,該值等於3+[(5*CPU_Count)/8])。

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

  • 說明:新生代並行回收收集器也是使用複製算法的收集器。它和並行收集器一樣,都是多線程、獨佔式的收集器。
  • 特點:它非常關注系統的吞吐量。
  • 使用方式:-XX:+UseParallelGC:新生代使用並行回收收集器,老年代使用串行收集器。
    -XX:+UseParallelOldGC:新生代和老年代都使用並行回收收集器。
    -XX:MaxGCPauseMillis:設置最大垃圾收集停頓時間,它的值是一個大於0的整數。收集器在工作是,會調整Java堆大小或者其他一些參數,儘可能地把停頓時間控制在MaxGCPauseMillis以內。如果希望減少停頓時間,而把這個值設置得很小,爲了達到預期的停頓時間,JVM可能會使用一個較小的堆,而這將導致垃圾回收變得很頻繁,從而增加了垃圾回收總時間,降低了吞吐量。
    -XX:GCTimeRatio:設置吞吐量大小,它的值是一個0~100之間的整數。假設GCTimeRatio的值爲n,那麼系統將花費不超過1/(1+n)的時間用於垃圾收集。比如GCTimeRatio等於19(默認值),則系統用於垃圾收集的時間不超過1/(1+19)=5%。默認情況下,它的取值是99,即不超過1%的時間用於垃圾回收。
  • 不同點:並行回收收集器與並行收集器比較,它支持一種自適應的GC調節策略。
    -XX:+UseAdaptiveSizePolicy 可以打開自適應GC策略。在這種模式下,新生代的大小、eden和survivor的比例、晉升老年代的對象年齡等參數會被自動調節,以達到在堆大小、吞吐量和停頓時間之間的平衡點。在手工調優比較困難的場合,可以僅指定虛擬機的最大堆、目標的吞吐量、和停頓時間,讓虛擬機自己完成調優工作。

9.老年代並行回收收集器

  • 說明:老年代的並行回收收集器也是一種多線程併發的收集器。和新生代並行回收收集器一樣,它也是一種關注吞吐量的收集器。老年代並行回收收集器使用標記-壓縮算法,它在JDK1.6中才可使用。
  • 使用方式:-XX:ParallelOldGC 可以在新生代和老年代都使用並行回收收集器。
    -XX:ParallelGCThreads 用於設置垃圾回收時的線程數量。
  • 特點:這是一對非常關注吞吐量的垃圾收集器組合,在對吞吐量敏感的系統中,可以考慮使用。

10.CMS收集器

  • 說明:與並行回收收集器不同,CMS收集器主要關注與系統停頓時間。CMS是Concurrent Mark Sweep的縮寫,意爲併發標記清除。它使用的是標記-清除算法,同時它又是一個使用多線程並行回收的垃圾收集器。
  • 工作過程:主要步驟有:初始標記、併發標記、重新標記、併發清除和併發重置。其中初始標記和重新標記是獨佔系統資源,而併發標記、併發清除和併發重置是可以和用戶線程一起執行。因此,從整體上來說,CMS收集不是獨佔式的,它可以在應用程序運行過程中進行來及回收。

    根據標記-清除算法,初始標記、併發標記和重新標記都是爲了標記處需要回收的對象。併發清理,則是在標記完成後,正式回收垃圾對象;併發重置是指在垃圾回收完成後,重新初始化CMS數據結構和數據,爲下一次垃圾回收做好準備。併發標記、併發清理和併發重置都是可以和應用程序線程一起執行的。
    CMS收集器在其主要的工作階段雖然沒有暴力地徹底暫停應用程序線程,但是由於它和應用程序線程併發執行,相互搶佔CPU,故在CMS執行期內對應用程序吞吐量將造成一定影響。CMS默認啓動的線程數是(ParallelGCThreads+3)/4,ParallelGCThreads是新生代並行收集器的線程數,也可以通過 -XX:ParallelCMSThreads參數手工設置CMS的線程數量。當CPU資源比較緊張時,受到CMS收集器線程的影響,影響系統的性能在垃圾回收階段可能會非常糟糕。
    缺點:由於CMS收集器不是獨佔式的回收器,在CMS回收過程中,應用程序仍然在不停地工作。在工作過程中,又會不斷地產生垃圾。這些新生產的垃圾在當前CMS回收過程中是無法清除的。同時,因爲應用程序沒有中斷,故在CMS回收過程中,還應該確保有足夠的內存可用。因此,CMS收集器不會等待堆內存飽和時才進行垃圾回收,而是當堆內存使用率達到某一閾值時,便開始進行回收。這個回收閾值可以使用 -XX:CMSInitiatingOccupancyFraction來指定。默認是68.即當老年代的空間使用率達到68%時,會執行一次CMS回收。如果應用程序的內存使用率增長很快,在CMS的執行過程中,已經出現了內存不足的情況,此時,CMS回收就會失敗,JVM將啓動老年代串行收集器進行垃圾回收。如果這樣,應用程序將完全中斷,直到垃圾收完成,這時,應用程序的停頓時間可能很長。
  • 使用方式:-XX:CMSInitiatingOccupancyFraction 如果內存增長緩慢,則可以設置一個稍大的值,大的閾值可以有效降低CMS的除法頻率,減少老年代回收的次數可以較爲明顯地改善應用程序性能。反之,如果應用程序內存使用率增長很快,則應該降低這個閾值,以避免頻繁觸發老年代串行收集器。
    -XX:+UseCMSCompactAtFullCollection 開關可以使CMS在垃圾回收完成後,進行一次內存碎片整理。內存碎片整理不是併發進行的。
    -XX:CMSFullGCsBeforeCompaction 指定多少次CMS回收後,進行一次內存壓縮。

11.G1收集器(Garbage First)

  • 說明:G1收集器在JDK1.6_Update14中提供了早期預覽版,並伴隨JDK7發佈。G1收集器的目標是作爲一款服務端的垃圾收集器,因此,它在吞吐量和停頓控制上,預期要優於CMS收集器。該收集器是基於標記-壓縮算法。
  • 優點:1.不會產生空間碎片。2.不需要進行獨佔式的碎片整理。3.可以進行非常精準的停頓控制。在指定長度爲M的時間段中,垃圾回收時間不超過N。
  • 使用方式:-XX:+UnlockExperimentalVMOptions
    -XX:+UseG1GC
    -XX:MaxGCPauseMillis=50
    -XX:GCPauseIntervalMillis=200 意爲在200ms內,停頓時間不超過50ms。這兩個參數是GL回收器的目標,但並不保證能執行它們。

12.總結

1.與串行回收器相關的參數

-XX:UseSerialGC:在新生代和老年代使用串行收集器。
-XX:SurvivorRatio:設置eden區大小和survivor區大小的比例。
-XX:PretenureSizeThreshold:設置大對象直接進入老年代的閾值。當對象的大小超過這個值時,直接在老年代分配。
-XX:MaxTenuringThreshold:設置對象進入老年代的年齡的最大值。每一次Minor GC後,對象的年齡就加一,任何大於這個年齡的對象,一定會進入老年代。

2.與並行GC相關的參數

-XX:+UseParNewGC 在新生代使用並行收集器
-XX:+UseParallelOldGC 老年代使用並行回收收集器
-XX:ParallelGCThreads 設置用於垃圾回收的線程數。通常情況下可以和CPU數量相等。但在CPU數量比較多的情況下,設置相對較小的數值也是合理的。
-XX:MaxGCPauseMillis 設置最大垃圾收集停頓時間。它的值是一個大於0的整數。收集器在工作時,會調整Java堆大小或者其他一些參數,儘可能地把停頓時間控制在MaxGCPauseMillis以內。
-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:+UseCMSCompactAtFullCollection 設置CMS收集器在完成垃圾收集後是否進行一次碎片整理。
-XX:CMSFullGCsBeforeCompaction 設定多少次CMS垃圾回收後進行一次內存壓縮。
-XX:+CMSClassUnloadingEnabled 允許對類元數據進行回收。
-XX:+CMSParallelRemarkEnabled 啓用並行重標記。
-XX:CMSInitiatingPermOccupancyFraction 當永久區佔用率達到這一百分比時,啓動CMS回收(前提是 -XX:+CMSClassUnloadingEnabled激活了)。
-XX:UseCMSInitiatingOccupancyOnly 表示只在到達閾值的時候,才進行CMS回收。
-XX:CMSIncrementalMode 使用增量模式,比較適合單CPU。

4.與G1回收器相關的參數

-XX:+UseG1GC 使用G1回收器。
-XX:+UnlockExperimentalVMOptions 允許使用實驗性參數。
-XX:MaxGCPauseMillis 設置最大垃圾收集停頓時間。
-XX:GCPauseIntervalMillis 設置停頓時間間隔。

5.其他參數

-XX:+DisableExplicitGC 禁用顯示GC。
-XX:MinHeapFreeRatio 設置堆空間最小空閒比例,默認是40,當堆空間的空閒內存小於這個數值時,JVM便會擴展堆空間。
-XX:MaxHeapFreeRatio 設置堆空間的最大空閒比例,默認是70。當堆空間的空閒內存大於這個數值時,便會壓縮堆空間,得到一個較小的堆。(當-Xms和-Xmx相等時,-XX:MinHeapFreeRatio和-XX:MaxHeapFreeRatio是無效的
-XX:LargePageSizeInBytes 設置大頁的大小。
-XX:TargetSurvivorRatio 設置survivor區的可使用率。這裏設置爲90%,則允許90%的survivor空間被使用。默認值是50%。故該設置提高了survivor區的使用率。當存放的對象超過這個百分比,則對象會向老年代壓縮。因此,這個選項更有助於將對象留在新生代。

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