1. 程式人生 > >JVM記憶體配置引數、GC工作原理及Minor GC、FullGC

JVM記憶體配置引數、GC工作原理及Minor GC、FullGC

對於JVM記憶體配置引數:
-Xmx10240m -Xms10240m -Xmn5120m -XXSurvivorRatio=3
,其最小記憶體值和Survivor區總大小分別是()
  • 5120m,1024m
  • 5120m,2048m
  • 10240m,1024m
  • 10240m,2048m

D-Xms10240m 初始堆大小即最小記憶體值 
XXSurvivorRatio=3,即Eden:FromSurvivor:ToSurvivor=3:1:1;所以Survivor一共是2048
-Xmx10240m:代表最大堆 -Xms10240m:代表最小堆 s是指small -Xmn5120m:代表新生代 n是指new -XXSurvivorRatio=3:代表Eden:Survivor = 3    根據Generation-Collection演算法(目前大部分JVM採用的演算法),一般根據物件的生存週期將堆記憶體分為若干不同的區域,一般情況將新生代分為Eden
 ,兩塊Survivor;    計算Survivor大小, Eden:Survivor = 3,總大小為5120,3x+x+x=5120  x=1024新生代大部分要回收,採用Copying演算法,快!老年代 大部分不需要回收,採用Mark-Compact演算法題目來自:newcoder

下面有關JVM記憶體,說法錯誤的是?


A   程式計數器是一個比較小的記憶體區域,用於指示當前執行緒所執行的位元組碼執行到了第幾行,是執行緒隔離的
B   Java方法執行記憶體模型,用於儲存區域性變數,運算元棧,動態連結,方法出口等資訊,是執行緒隔離的
C   方法區用於儲存JVM載入的類資訊、常量、靜態變數、即使編譯器編譯後的程式碼等資料,是執行緒隔離的
D   原則上講,所有的物件都在堆區上分配記憶體,是執行緒之間共享的


答案:  C

分析:首先來說這個答案是很容易選出來的,只要知道靜態變數是屬於類的,在方法中是沒有靜態變數的,就可以選出正確答案了,但這兒還要把A C D說一下。大多數 JVM 將記憶體區域劃分為 Method Area(Non-Heap)(方法區) ,Heap(堆) , Program Counter Register(程式計數器) ,   VM Stack(虛擬機器棧,也有翻譯成JAVA 方法棧的),Native Method Stack  ( 本地方法棧 ),其中Method Area 和  Heap 是執行緒共享的  ,VM Stack,Native Method Stack  和Program Counter Register  是非執行緒共享的(每個執行緒執行的時候都會單獨的建立)。方法區在JVM中也是一個非常重要的區域,它與堆一樣,是被 執行緒共享 的區域。 在方法區中,儲存了每個類的資訊(包括類的名稱、方法資訊、欄位資訊)、靜態變數、常量以及編譯器編譯後的程式碼等。


JVM記憶體管理和JVM垃圾回收

JVM記憶體組成結構

JVM記憶體結構由堆、棧、本地方法棧、方法區等部分組成,結構圖如下所示:

JVM記憶體組成結構

1)堆

所有通過new建立的物件的記憶體都在堆中分配,其大小可以通過-Xmx和-Xms來控制。堆被劃分為新生代和舊生代,新生代又被進一步劃分為Eden和Survivor區,最後Survivor由FromSpace和ToSpace組成,結構圖如下所示:

JVM記憶體結構之堆

新生代。新建的物件都是用新生代分配記憶體,Eden空間不足的時候,會把存活的物件轉移到Survivor中,新生代大小可以由-Xmn來控制,也可以用-XX:SurvivorRatio來控制Eden和Survivor的比例。舊生代用於存放新生代中經過多次垃圾回收(也即Minor GC)仍然存活的物件

2)棧

每個執行緒執行每個方法的時候都會在棧中申請一個棧幀,每個棧幀包括區域性變數區和運算元棧,用於存放此次方法呼叫過程中的臨時變數、引數和中間結果

3)本地方法棧

用於支援native方法的執行,儲存了每個native方法呼叫的狀態

4)方法區

存放了要載入的類資訊、靜態變數、final型別的常量、屬性和方法資訊。JVM用持久代(PermanetGeneration)來存放方法區,可通過-XX:PermSize和-XX:MaxPermSize來指定最小值和最大值。介紹完了JVM記憶體組成結構,下面我們再來看一下JVM垃圾回收機制。

JVM垃圾回收機制

JVM分別對新生代和舊生代採用不同的垃圾回收機制

新生代的GC:

新生代通常存活時間較短,因此基於Copying演算法來進行回收,所謂Copying演算法就是掃描出存活的物件,並複製到一塊新的完全未使用的空間中,對應於新生代,就是在Eden和FromSpace或ToSpace之間copy。新生代採用空閒指標的方式來控制GC觸發,指標保持最後一個分配的物件在新生代區間的位置,當有新的物件要分配記憶體時,用於檢查空間是否足夠,不夠就觸發GC。當連續分配物件時,物件會逐漸從eden到survivor,最後到舊生代,

用javavisualVM來檢視,能明顯觀察到新生代滿了後,會把物件轉移到舊生代,然後清空繼續裝載,當舊生代也滿了後,就會報outofmemory的異常,如下圖所示:

outofmemory的異常

在執行機制上JVM提供了序列GC(SerialGC)、並行回收GC(ParallelScavenge)和並行GC(ParNew)

1)序列GC

在整個掃描和複製過程採用單執行緒的方式來進行,適用於單CPU、新生代空間較小及對暫停時間要求不是非常高的應用上,是client級別預設的GC方式,可以通過-XX:+UseSerialGC來強制指定

2)並行回收GC

在整個掃描和複製過程採用多執行緒的方式來進行,適用於多CPU、對暫停時間要求較短的應用上,是server級別預設採用的GC方式,可用-XX:+UseParallelGC來強制指定,用-XX:ParallelGCThreads=4來指定執行緒數

3)並行GC

與舊生代的併發GC配合使用

舊生代的GC:

舊生代與新生代不同,物件存活的時間比較長,比較穩定,因此採用標記(Mark)演算法來進行回收,所謂標記就是掃描出存活的物件,然後再進行回收未被標記的物件,回收後對用空出的空間要麼進行合併,要麼標記出來便於下次進行分配,總之就是要減少記憶體碎片帶來的效率損耗。在執行機制上JVM提供了序列GC(SerialMSC)、並行GC(parallelMSC)和併發GC(CMS),具體演算法細節還有待進一步深入研究。

以上各種GC機制是需要組合使用的,指定方式由下表所示:

GC機制組合使用

本文轉自:http://developer.51cto.com/art/201103/248642.htm
對於Minor GC 和 Full GC的解釋:

  •  新生代 GC(Minor GC):指發生在新生代的垃圾收集動作,因為 Java 物件大多都具
    備朝生夕滅的特性,所以 Minor GC 非常頻繁,一般回收速度也比較快。
  •  老年代 GC(Major GC  / Full GC):指發生在老年代的 GC,出現了 Major GC,經常
    會伴隨至少一次的 Minor GC(但非絕對的,在 ParallelScavenge 收集器的收集策略裡
    就有直接進行 Major GC 的策略選擇過程) 。MajorGC 的速度一般會比 Minor GC 慢 10
    倍以上。

虛擬機器給每個物件定義了一個物件年齡(Age)計數器。如果物件在 Eden 出生並經過第一次 Minor GC 後仍然存活,並且能被 Survivor 容納的話,將被移動到 Survivor 空間中,並將物件年齡設為 1。物件在 Survivor 區中每熬過一次 Minor GC,年齡就增加 1 歲,當它的年齡增加到一定程度(預設為 15 歲)時,就會被晉升到老年代中。物件晉升老年代的年齡閾值,可以通過引數 -XX:MaxTenuringThreshold 來設定。


=======================================

JVMHeap區域(年輕代、老年代)和方法區(永久代)結構圖:


從Java GC的角度解讀程式碼:程式20行new的Person物件會首先會進入年輕代Eden中(如果物件太大可能直接進入年老代)。在miner GC之前物件是存在Eden和from中的,進行miner GC的時候Eden中的物件被拷貝到To這樣一個survive空間(survive倖存)空間:包括from和to,他們的空間大小是一樣的,又叫s1和s2)中(有一個拷貝演算法),From中的物件(演算法會考慮經過GC倖存的次數)到一定次數(閾值(如果說每次GC之後這個物件依舊在Survive中存在,GC一次他的Age就會加1,預設15就會放到OldGeneration。但是實際情況比較複雜,有可能沒有到閾值就從Survive區域直接到Old Generation區域。在進行GC的時候會對Survive中的物件進行判斷,Survive空間中有一些物件Age是一樣的,也就是經過的GC次數一樣,年齡相同的這樣一批物件的總和大於等於Survive空間一半的話,這組物件就會進入old Generation中,(是一種動態的調整))),會被複制到OldGeneration,如果沒到次數From中的物件會被複制到To中,複製完成後To中儲存的是有效的物件,Eden和From中剩下的都是無效的物件,這個時候就把Eden和From中所有的物件清空。在複製的時候Eden中的物件進入To中,To可能已經滿了,這個時候Eden中的物件就會被直接複製到Old Generation中,From中的物件也會直接進入Old Generation中。就是存在這樣一種情況,To比較小,第一次複製的時候空間就滿了,直接進入old Generation中。複製完成後,To和From的名字會對調一下,因為Eden和From都是空的,對調後Eden和To都是空的,下次分配就會分配到Eden。一直迴圈這個流程。好處:使用物件最多和效率最高的就是在Young Generation中,通過From to就避免過於頻繁的產生FullGC(Old Generation滿了一般都會產生FullGC)

虛擬機器在進行MinorGC(新生代的GC)的時候,會判斷要進入OldGeneration區域物件的大小,是否大於Old Generation剩餘空間大小,如果大於就會發生Full GC

剛分配物件在Eden中,如果空間不足嘗試進行GC,回收空間,如果進行了MinorGC空間依舊不夠就放入Old Generation,如果OldGeneration空間還不夠就OOM了。

比較大的物件,陣列等,大於某值(可配置)就直接分配到老年代,(避免頻繁記憶體拷貝)

年輕代和年老代屬於Heap空間的

Permanent Generation(永久代)可以理解成方法區,(它屬於方法區)也有可能發生GC,例如類的例項物件全部被GC了,同時它的類載入器也被GC掉了,這個時候就會觸發永久代中物件的GC。

如果OldGeneration滿了就會產生FullGC

物件進入Old Generation的原因:

1,from survive中物件的生命週期到一定閾值

2,分配的物件直接是大物件

3、由於To 空間不夠,進行miner GC直接把物件拷貝到年老代(年老代GC時候採用不同的演算法)

如果Young Generation大小分配不合理或空間比較小,這個時候導致物件很容易進入Old Generation中,而Old Generation中回收具體物件的時候速度是遠遠低於Young Generation回收速度。

因此實際分配要考慮年老代和新生代的比例,考慮Eden和survives的比例

Permanent Generation中發生GC的時候也對效能影響非常大,也是Full GC

====================================

minor gc/full gc的觸發條件、OOM的觸發條件,降低GC的調優的策略

    eden滿了minor gc,升到老年代的物件大於老年代剩餘空間full gc,或者小於時被HandlePromotionFailure引數強制full gc;gc與非gc時間耗時超過了GCTimeRatio的限制引發OOM,調優諸如通過NewRatio控制新生代老年代比例,通過 MaxTenuringThreshold控制進入老年前生存次數等

判定gc要回收的物件(上文都沒說到怎麼判定一個物件為無效)

     從root搜尋不到,而且經過第一次標記、清理後,仍然沒有復活的物件。

minor gc/full gc的清理演算法

     miner gc使用的是複製清理,full gc使用的是標記清理

==========================

垃圾收集器型別

序列回收器(Serial Collector)

單執行緒執行回收操作,回收期間暫停所有應用執行緒的執行,client模式下的預設回收器,通過-XX:+UseSerialGC命令列可選項強制指定。

  • 年輕代的回收演算法(Minor Collection)
    把Eden區的存活物件移到To區,To區裝不下直接移到年老代,把From區的移到To區,To區裝不下直接移到年老代,From區裡面年齡很大的升級到年老代。 回收結束之後,Eden和From區都為空,此時把From和To的功能互換,From變To,To變From,每一輪迴收之前To都是空的。設計的選型為複製。
  • 年老代的回收演算法(Full Collection)
    年老代的回收分為三個步驟,標記(Mark)、清除(Sweep)、合併(Compact)。標記階段把所有存活的物件標記出來,清除階段釋放所有死亡的物件,合併階段 把所有活著的物件合併到年老代的前部分,把空閒的片段都留到後面。設計的選型為合併,減少記憶體的碎片。

並行回收器(Parallel Collector)

使用多個執行緒同時進行垃圾回收,多核環境裡面可以充分的利用CPU資源,減少回收時間,增加JVM生產率,Server模式下的預設回收器。與序列回收器相同,回收期間暫停所有應用執行緒的執行。通過-XX:+UseParallelGC命令列可選項強制指定。

  • 年輕代的回收演算法(Minor Collection)
    使用多個執行緒回收垃圾,每一個執行緒的演算法與序列回收器相同。
  • 年老代的回收演算法(Full Collection)
    年老代依然是單執行緒的,與序列回收器相同。

並行合併收集器(Parallel Compacting Collection)

年輕代和年老代的回收都是用多執行緒處理。通過命令可選項-XX:+UseParallelOldGC指定,–XX:ParallelGCThreads=3還可進一步指定參與並行回收的執行緒數。與序列回收器相同,回收期間暫停所有應用執行緒的執行。與並行回收器相比,年老代的回收時間更短,從而減少了暫停時間間隔(Pause time)。通過–XX:+UseParallelOldGC命令列可選項強制指定。

  • 年輕代的回收演算法(Minor Collection)
    與並行回收器(Parallel Collector)相同
  • 年老代的回收演算法(Full Collection) 
    年老代分為三個步驟,標記、統計、合併。這裡用到分的思想,把年老代劃分為很多個固定大小的區(region)。 標記階段,把所有存活的物件劃分為N組(應該與回收執行緒數相同),每一個執行緒獨立的負責自己那一組,標記存活物件的位置以及 所在區(Region)的存活率資訊,標記為並行的。統計階段,統計每一個區(Region)的存活率,原則上靠前面的存活率較高,從前到後, 找到值得合併的開始位置(絕大多數物件都存活的區不值得合併),統計階段是序列的(單執行緒)。合併階段,依據統計階段的資訊,多執行緒 並行的把存活的物件從一個區(Region)複製到另外一個區(Region)。

併發標記清除回收器(Concurrent Mark-Sweep Collector)

又名低延時收集器(Low-latency Collector),通過各種手段使得應用程式被掛起的時間最短。基本與應用程式併發地執行回收操作,沒有合併和複製操作。通過命令列-XX:+UseConcMarkSweepGC指定,在單核或者雙核系統裡面還可以指定使用增量式回收模式-XX:+UseConcMarkSweepGC。增量式回收是指把回收操作分為多個片段,執行一個片段之後釋放CPU資源給應用程式,未來的某個時點接著上次的結果繼續回收下去。目的也是減少延時。

  • 年輕代的回收演算法(Minor Collection)
    與並行回收器(Parallel Collector)相同
  • 年老代的回收演算法(Full Collection) 
    分為四個步驟,初始標記(Initial Mark)、併發標記(Concurrent Mark)、再次標記(Remark)、以及併發清理(Concurrent Sweep)。特別注意,沒有合併操作,所以會有碎片。
  • 初始化階段: 暫停應用執行緒,找出所有存活的物件,耗時比較短,回收器使用單執行緒。
  • 併發標記階段: 回收器標記操作與應用併發執行,回收器使用單執行緒標記存活物件。
  • 再次標記:併發標記階段由於應用程式也在執行,這個過程中可能新增或者修改物件。所以再次暫停應用執行緒,找出所有修改的物件,使用多執行緒標記。
  • 併發清理:回收器清理操作與應用併發執行,回收器使用單執行緒清理死亡物件。

==========================================

HotSpot虛擬機器中有7種垃圾收集器:Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS、G1,先介紹一些垃圾收集的相關概念,再介紹它們的主要特點、應用場景、以及一些設定引數和基本執行原理。

1、垃圾收集器概述

垃圾收集器是垃圾回收演算法(標記-清除演算法、複製演算法、標記-整理演算法、火車演算法)的具體實現,不同商家、不同版本的JVM所提供的垃圾收集器可能會有很在差別,本文主要介紹HotSpot虛擬機器中的垃圾收集器。

1.1、垃圾收集器組合

JDK7/8後,HotSpot虛擬機器所有收集器及組合(連線),如下圖:

(A)、圖中展示了7種不同分代的收集器

Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS、G1;

(B)、而它們所處區域,則表明其是屬於新生代收集器還是老年代收集器:

新生代收集器:Serial、ParNew、Parallel Scavenge;

老年代收集器:Serial Old、Parallel Old、CMS

整堆收集器G1

(C)、兩個收集器間有連線,表明它們可以搭配使用

Serial/Serial Old、Serial/CMS、ParNew/Serial Old、ParNew/CMS、Parallel Scavenge/Serial Old、Parallel Scavenge/Parallel Old、G1;

(D)、其中Serial Old作為CMS出現"Concurrent Mode Failure"失敗的後備預案(後面介紹);

1.2、併發垃圾收集和並行垃圾收集的區別

(A)、並行(Parallel)

多條垃圾收集執行緒並行工作,但此時使用者執行緒仍然處於等待狀態

ParNew、Parallel Scavenge、Parallel Old

(B)、併發(Concurrent)

使用者執行緒與垃圾收集執行緒同時執行(但不一定是並行的,可能會交替執行);

使用者程式在繼續執行,而垃圾收集程式執行緒運行於另一個CPU上;    

CMS、G1(也有並行);

1.3、Minor GC和Full GC的區別

(A)、Minor GC

又稱新生代GC,指發生在新生代的垃圾收集動作;

因為Java物件大多是朝生夕滅,所以Minor GC非常頻繁,一般回收速度也比較快

(B)、Full GC

又稱Major GC或老年代GC,指發生在老年代的GC;

出現Full GC經常會伴隨至少一次的Minor GC(不是絕對,Parallel Sacvenge收集器就可以選擇設定Major GC策略);

Major GC速度一般比Minor GC慢10倍以上

下面將介紹這些收集器的特性、基本原理和使用場景,並重點分析CMS和G1這兩款相對複雜的收集器;但需要明確一個觀點:

沒有最好的收集器,更沒有萬能的收集;

選擇的只能是適合具體應用場景的收集器

2、Serial收集器

Serial(序列)垃圾收集器是最基本、發展歷史最悠久的收集器;

JDK1.3.1前是HotSpot新生代收集的唯一選擇;

2.1、特點

針對新生代;

採用複製演算法;

單執行緒收集;

進行垃圾收集時,必須暫停所有工作執行緒,直到完成;            

即會"Stop The World"

Serial/Serial Old組合收集器執行示意圖如下:

2.2、應用場景

依然是HotSpot在Client模式下預設的新生代收集器

也有優於其他收集器的地方:

簡單高效(與其他收集器的單執行緒相比);

對於限定單個CPU的環境來說,Serial收集器沒有執行緒互動(切換)開銷,可以獲得最高的單執行緒收集效率;

使用者的桌面應用場景中,可用記憶體一般不大(幾十M至一兩百M),可以在較短時間內完成垃圾收集(幾十MS至一百多MS),只要不頻繁發生,這是可以接受的

2.3、設定引數

"-XX:+UseSerialGC":新增該引數來顯式的使用序列垃圾收集器;

2.4、Stop TheWorld說明

JVM在後臺自動發起和自動完成的,在使用者不可見的情況下,把使用者正常的工作執行緒全部停掉,即GC停頓

會帶給使用者不良的體驗;

從JDK1.3到現在,從Serial收集器-》Parallel收集器-》CMS-》G1,使用者執行緒停頓時間不斷縮短,但仍然無法完全消除

更多"Stop The World"資訊請參考:《Java虛擬機器垃圾回收(一) 基礎》"2-2、可達性分析演算法"

更多Serial收集器請參考:

3、ParNew收集器

ParNew垃圾收集器是Serial收集器的多執行緒版本

3.1、特點

除了多執行緒外,其餘的行為、特點和Serial收集器一樣

如Serial收集器可用控制引數、收集演算法、Stop The World、記憶體分配規則、回收策略等;

兩個收集器共用了不少程式碼;

ParNew/Serial Old組合收集器執行示意圖如下:

3.2、應用場景

Server模式下,ParNew收集器是一個非常重要的收集器,因為除Serial外,目前只有它能與CMS收集器配合工作

但在單個CPU環境中,不會比Serail收集器有更好的效果,因為存線上程互動開銷。

3.3、設定引數

"-XX:+UseConcMarkSweepGC":指定使用CMS後,會預設使用ParNew作為新生代收集器;

"-XX:+UseParNewGC":強制指定使用ParNew;    

"-XX:ParallelGCThreads":指定垃圾收集的執行緒數量,ParNew預設開啟的收集執行緒與CPU的數量相同;

3.4、為什麼只有ParNew能與CMS收集器配合

CMS是HotSpot在JDK1.5推出的第一款真正意義上的併發(Concurrent)收集器,第一次實現了讓垃圾收集執行緒與使用者執行緒(基本上)同時工作;

CMS作為老年代收集器,但卻無法與JDK1.4已經存在的新生代收集器Parallel Scavenge配合工作;

因為Parallel Scavenge(以及G1)都沒有使用傳統的GC收集器程式碼框架,而另外獨立實現;而其餘幾種收集器則共用了部分的框架程式碼;

關於CMS收集器後面會詳細介紹。

4、Parallel Scavenge收集器

Parallel Scavenge垃圾收集器因為與吞吐量關係密切,也稱為吞吐量收集器(Throughput Collector)

4.1、特點

(A)、有一些特點與ParNew收集器相似

新生代收集器;

採用複製演算法;

多執行緒收集;

(B)、主要特點是:它的關注點與其他收集器不同

CMS等收集器的關注點是儘可能地縮短垃圾收集時使用者執行緒的停頓時間;

而Parallel Scavenge收集器的目標則是達一個可控制的吞吐量(Throughput)

關於吞吐量與收集器關注點說明詳見本節後面;

4.2、應用場景

高吞吐量為目標,即減少垃圾收集時間,讓使用者程式碼獲得更長的執行時間;

當應用程式執行在具有多個CPU上,對暫停時間沒有特別高的要求時,即程式主要在後臺進行計算,而不需要與使用者進行太多互動

例如,那些執行批量處理、訂單處理、工資支付、科學計算的應用程式

4.3、設定引數

Parallel Scavenge收集器提供兩個引數用於精確控制吞吐量:

(A)、"-XX:MaxGCPauseMillis"

控制最大垃圾收集停頓時間,大於0的毫秒數;

MaxGCPauseMillis設定得稍小,停頓時間可能會縮短,但也可能會使得吞吐量下降;

因為可能導致垃圾收集發生得更頻繁;

(B)、"-XX:GCTimeRatio"

設定垃圾收集時間佔總時間的比率,0<n<100的整數;

GCTimeRatio相當於設定吞吐量大小

垃圾收集執行時間佔應用程式執行時間的比例的計算方法是:

1 / (1 + n)

例如,選項-XX:GCTimeRatio=19,設定了垃圾收集時間佔總時間的5%--1/(1+19);

預設值是1%--1/(1+99),即n=99;

垃圾收集所花費的時間是年輕一代和老年代收集的總時間;

如果沒有滿足吞吐量目標,則增加代的記憶體大小以儘量增加使用者程式執行的時間;

此外,還有一個值得關注的引數:

(C)、"-XX:+UseAdptiveSizePolicy"

開啟這個引數後,就不用手工指定一些細節引數,如:

新生代的大小(-Xmn)、Eden與Survivor區的比例(-XX:SurvivorRation)、晉升老年代的物件年齡(-XX:PretenureSizeThreshold)等;

JVM會根據當前系統執行情況收集效能監控資訊,動態調整這些引數,以提供最合適的停頓時間或最大的吞吐量,這種調節方式稱為GC自適應的調節策略(GC Ergonomiscs);    

這是一種值得推薦的方式

(1)、只需設定好記憶體資料大小(如"-Xmx"設定最大堆);

(2)、然後使用"-XX:MaxGCPauseMillis"或"-XX:GCTimeRatio"給JVM設定一個優化目標;

(3)、那些具體細節引數的調節就由JVM自適應完成;        

這也是Parallel Scavenge收集器與ParNew收集器一個重要區別;    

更多目標調優和GC自適應的調節策略說明請參考:            

4.4、吞吐量與收集器關注點說明

(A)、吞吐量(Throughput)

CPU用於執行使用者程式碼的時間與CPU總消耗時間的比值;

吞吐量=執行使用者程式碼時間/(執行使用者程式碼時間+垃圾收集時間);    

高吞吐量即減少垃圾收集時間,讓使用者程式碼獲得更長的執行時間;

(B)、垃圾收集器期望的目標(關注點)

(1)、停頓時間    

停頓時間越短就適合需要與使用者互動的程式;

良好的響應速度能提升使用者體驗;

(2)、吞吐量

高吞吐量則可以高效率地利用CPU時間,儘快完成運算的任務

主要適合在後臺計算而不需要太多互動的任務;

(3)、覆蓋區(Footprint)

在達到前面兩個目標的情況下,儘量減少堆的記憶體空間

可以獲得更好的空間區域性性;

更多Parallel Scavenge收集器的資訊請參考:

上面介紹的都是新生代收集器,接下來開始介紹老年代收集器;

5、Serial Old收集器

Serial Old是 Serial收集器的老年代版本

5.1、特點

針對老年代;

採用"標記-整理"演算法(還有壓縮,Mark-Sweep-Compact);

單執行緒收集;

Serial/Serial Old收集器執行示意圖如下:

5.2、應用場景

主要用於Client模式;

而在Server模式有兩大用途:

(A)、在JDK1.5及之前,與Parallel Scavenge收集器搭配使用(JDK1.6有Parallel Old收集器可搭配);

(B)、作為CMS收集器的後備預案,在併發收集發生Concurrent Mode Failure時使用(後面詳解);

更多Serial Old收集器資訊請參考:

6、Parallel Old收集器

Parallel Old垃圾收集器是Parallel Scavenge收集器的老年代版本

JDK1.6中才開始提供;

6.1、特點

針對老年代;

採用"標記-整理"演算法;

多執行緒收集;

Parallel Scavenge/Parallel Old收集器執行示意圖如下:

6.2、應用場景

JDK1.6及之後用來代替老年代的Serial Old收集器;

特別是在Server模式,多CPU的情況下;

這樣在注重吞吐量以及CPU資源敏感的場景,就有了Parallel Scavenge加Parallel Old收集器的"給力"應用組合

6.3、設定引數

"-XX:+UseParallelOldGC":指定使用Parallel Old收集器;

更多Parallel Old收集器收集過程介紹請參考:

7、CMS收集器

併發標記清理(Concurrent Mark Sweep,CMS)收集器也稱為併發低停頓收集器(Concurrent Low Pause Collector)或低延遲(low-latency)垃圾收集器

在前面ParNew收集器曾簡單介紹過其特點;

7.1、特點

針對老年代;

基於"標記-清除"演算法(不進行壓縮操作,產生記憶體碎片);            

以獲取最短回收停頓時間為目標;

併發收集、低停頓;

需要更多的記憶體(看後面的缺點);

是HotSpot在JDK1.5推出的第一款真正意義上的併發(Concurrent)收集器;

第一次實現了讓垃圾收集執行緒與使用者執行緒(基本上)同時工作;

7.2、應用場景

與使用者互動較多的場景;        

希望系統停頓時間最短,注重服務的響應速度;

以給使用者帶來較好的體驗;

常見WEB、B/S系統的伺服器上的應用

7.3、設定引數

"-XX:+UseConcMarkSweepGC":指定使用CMS收集器;

7.4、CMS收集器運作過程

比前面幾種收集器更復雜,可以分為4個步驟:

(A)、初始標記(CMS initial mark)

僅標記一下GC Roots能直接關聯到的物件;

速度很快;

但需要"Stop The World";

(B)、併發標記(CMS concurrent mark)

進行GC Roots Tracing的過程;

剛才產生的集合中標記出存活物件;

應用程式也在執行;

並不能保證可以標記出所有的存活物件;

(C)、重新標記(CMS remark)

為了修正併發標記期間因使用者程式繼續運作而導致標記變動的那一部分物件的標記記錄;

需要"Stop The World",且停頓時間比初始標記稍長,但遠比並發標記短;

採用多執行緒並行執行來提升效率;