java實戰--GC終極總結

GC簡介

java的最大好處是自動垃圾回收,這樣就無需我們手動的釋放對象空間了,但是也產生了相應的負效果,gc是需要時間和資源的,不好的gc會嚴重影響系統的系能,因此良好的gc是JVM的高性能的保證JVM堆分爲新生代,舊生代和年老代,新生代可用的gc方式有:串行gc(Serial Copying),並行回收gc(Parellel Scavenge),並行gc(ParNew),舊生代和年老代可用的gc方式有串行gc(Serial MSC),並行gc(Parallel MSC),併發gc(CMS)。

GC需要了解地方

1.哪些內存要回收

2.什麼時候回收

3.怎麼回收

哪些內存要回收

java內存模型中分爲五大區域已經有所瞭解。我們知道程序計數器虛擬機棧本地方法棧,由線程而生,隨線程而滅,其中棧中的棧幀隨着方法的進入順序的執行的入棧和出棧的操作,一個棧幀需要分配多少內存取決於具體的虛擬機實現並且在編譯期間即確定下來【忽略JIT編譯器做的優化,基本當成編譯期間可知】,當方法或線程執行完畢後,內存就隨着回收,因此無需關心。

Java堆方法區則不一樣。GC垃圾回收主要集中在堆和方法區,在程序運行期間,這部分內存的分配和使用都是動態的。方法區存放着類加載信息,但是一個接口中多個實現類需要的內存可能不太一樣,一個方法中多個分支需要的內存也可能不一樣【只有在運行期間纔可知道這個方法創建了哪些對象沒需要多少內存】,這部分內存的分配和回收都是動態的,gc關注的也正是這部分的內存。

Java堆是GC回收的“重點區域”,堆是 JVM 所管理的最大的一塊內存空間,主要用於存放各種類的實例對象。 堆中基本存放着所有對象實例,gc進行回收前,第一件事就是確認哪些對象存活,哪些死去[即不可能再被引用]

堆的回收區域

爲了高效的回收,jvm將堆分爲三個區域
1.新生代(Young Generation)NewSize和MaxNewSize分別可以控制年輕代的初始大小和最大的大小,1個Eden區和2個Survivor區(分別叫from和to)。默認比例爲8:1
2.老年代(Old Generation)
3.永久代(Permanent Generation)【1.8以後採用元空間,就不在堆中了】

堆大小 = 年輕代 + 老年代
年輕代 = eden space (新生代) + from survivor + to survivor堆大小 (兩個倖存者區域)
年輕代 = eden space (新生代) + from survivor + to survivor,其中ServivorTo:保留了一次MinorGC過程中的倖存者。
ServivorFrom:上一次GC的倖存者,作爲這一次GC的被掃描者,MinorGC的過程:MinorGC採用複製算法。在GC開始的時候,對象只會存在於Eden區和名爲“From”的Survivor區,Survivor區“To”是空的。緊接着進行GC,Eden區中所有存活的對象都會被複制到“To”,而在“From”區中,仍存活的對象會根據他們的年齡值來決定去向。年齡達到一定值(年齡閾值,可以通過-XX:MaxTenuringThreshold來設置)的對象會被移動到年老代中,沒有達到閾值的對象會被複制到“To”區域。經過這次GC後,Eden區和From區已經被清空。這個時候,“From”和“To”會交換他們的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎樣,都會保證名爲To的Survivor區域是空的。Minor GC會一直重複這樣的過程,直到“To”區被填滿,“To”區被填滿之後,會將所有對象移動到年老代中。但是JVM 每次只會使用 Eden 和其中的一塊 Survivor 區域來爲對象服務,所以無論什麼時候,總是有一塊 Survivor 區域是空閒着的。因此,新生代實際可用的內存空間爲 9/10 ( 即90% )的新生代空間。

年輕代用來存放新近創建的對象,尺寸隨堆大小的增大和減小而相應的變化,默認值是保持爲堆大小的1/15,可以通過 -Xmn 參數設置年輕代爲固定大小,也可以通過 -XX:NewRatio 來設置年輕代與老年代的大小比例,年輕代的特點是對象更新速度快,在短時間內產生大量的“死亡對象”。
 

GC 分爲兩種:Minor GC、Full GC ( 或稱爲 Major GC )


         新生代幾乎是所有 Java 對象出生的地方,即 Java 對象申請的內存以及存放都是在這個地方。Java 中的大部分對象通常不需長久存活,具有朝生夕滅的性質。 年輕代的特點是產生大量的死亡對象,並且要是產生連續可用的空間, 爲了提高內存的利用率所以採用複製回收算法進行垃圾回收.對年輕代的垃圾回收稱作初級回收 (minor gc)
        當一個對象被判定爲 "死亡" 的時候,GC 就有責任來回收掉這部分對象的內存空間。新生代是 GC 收集垃圾的頻繁區域。 當對象在 Eden 出生後,在經過一次 Minor GC 後,如果對象還存活,並且能夠被另外一塊 Survivor 區域所容納,則使用複製算法將這些仍然還存活的對象複製到另外一塊 Survivor 區域中,然後清理所使用過的 Eden 以及 Survivor 區域,並且將這些對象的年齡設置爲1,以後對象在 Survivor 區每熬過一次 Minor GC,就將對象的年齡 + 1,當對象的年齡達到某個值時 ( 默認是 15 歲,可以通過參數 -XX:MaxTenuringThreshold 來設定 ),這些對象就會成爲老年代。 


注意: 但這也不是一定的,對於一些較大的對象 ( 即需要分配一塊較大的連續內存空間 ) 則是直接進入到老年代。
老年代:

Full GC 是發生在老年代的垃圾收集動作,所採用的是標記-清除算法(這種算法可能會產生大量的內存碎片),或者標記整理算法(解決內存碎片問題)。現實的生活中,老年代的人通常會比新生代的人 "早死"。堆內存中的老年代(Old)不同於這個,老年代裏面的對象幾乎個個都是在 Survivor 區域中熬過來的,它們是不會那麼容易就 "死掉" 了的。因此,Full GC 發生的次數不會有 Minor GC 那麼頻繁並且做一次 Full GC 要比進行一次 Minor GC 的時間更長。 
另外,標記-清除算法收集垃圾的時候會產生許多的內存碎片 ( 即不連續的內存空間 ),此後需要爲較大的對象分配內存空間時,若無法找到足夠的連續的內存空間,就會提前觸發一次 GC 的收集動作,MajorGC採用標記—清除算法:首先掃描一次所有老年代,標記出存活的對象,然後回收沒有標記的對象。MajorGC的耗時比較長,因爲要掃描再回收。MajorGC會產生內存碎片,爲了減少內存損耗,我們一般需要進行合併或者標記出來方便下次直接分配。 當老年代也滿了裝不下的時候,就會拋出OOM(Out of Memory)異常

判斷對象是否存活算法

1.引用計數算法
早期判斷對象是否存活大多都是以這種算法,這種算法判斷很簡單,簡單來說就是給對象添加一個引用計數器,每當對象被引用一次就加1,引用失效時就減1。當爲0的時候就判斷對象不會再被引用。
優點:實現簡單效率高,被廣泛使用與如python何遊戲腳本語言上。
缺點:難以解決循環引用的問題,就是假如兩個對象互相引用已經不會再被其它其它引用,導致一直不會爲0就無法進行回收。

2.可達性分析算法
目前主流的商用語言[如java、c#]採用的是可達性分析算法判斷對象是否存活。這個算法有效解決了循環利用的弊端。
它的基本思路是通過一個稱爲“GC Roots”的對象爲起始點,搜索所經過的路徑稱爲引用鏈,當一個對象到GC Roots沒有任何引用跟它連接則證明對象是不可用的。

可作爲GC Roots的對象有四種

①虛擬機棧(棧楨中的本地變量表)中的引用的對象,就是平時所指的java對象,存放在堆中。
②方法區中的類靜態屬性引用的對象,一般指被static修飾引用的對象,加載類的時候就加載到內存中。
③方法區中的常量引用的對象,
④本地方法棧中JNI(native方法)引用的對象

即使可達性算法中不可達的對象,也不是一定要馬上被回收,還有可能被搶救一下。網上例子很多,基本上和深入理解JVM一書講的一樣對象的生存還是死亡

要真正宣告對象死亡需經過兩個過程。
1.可達性分析後沒有發現引用鏈
2.查看對象是否有finalize方法,如果有重寫且在方法內完成自救[比如再建立引用],還是可以搶救一下,注意這邊一個類的finalize只執行一次,這就會出現一樣的代碼第一次自救成功第二次失敗的情況。[如果類重寫finalize且還沒調用過,會將這個對象放到一個叫做F-Queue的序列裏,這邊finalize不承諾一定會執行,這麼做是因爲如果裏面死循環的話可能會時F-Queue隊列處於等待,嚴重會導致內存崩潰,這是我們不希望看到的。]

HotSpot虛擬機如何實現可達性算法

垃圾收集算法

jvm中,可達性分析算法幫我們解決了哪些對象可以回收的問題,垃圾收集算法則關心怎麼回收。

三大垃圾收集算法

1.標記/清除算法【最基礎】
2.複製算法
3.標記/整理算法
jvm採用`分代收集算法`對不同區域採用不同的回收算法。

參考GC算法深度解析

新生代採用複製算法

新生代中因爲對象都是"朝生夕死的",【深入理解JVM虛擬機上說98%的對象,不知道是不是這麼多,總之就是存活率很低】,適用於複製算法【複製算法比較適合用於存活率低的內存區域】。它優化了標記/清除算法的效率和內存碎片問題,且JVM不以5:5分配內存【由於存活率低,不需要複製保留那麼大的區域造成空間上的浪費,因此不需要按1:1【原有區域:保留空間】劃分內存區域,而是將內存分爲一塊Eden空間和From Survivor、To Survivor【保留空間】,三者默認比例爲8:1:1,優先使用Eden區,若Eden區滿,則將對象複製到第二塊內存區上。但是不能保證每次回收都只有不多於10%的對象存貨,所以Survivor區不夠的話,則會依賴老年代年存進行分配】。

GC開始時,對象只會存於Eden和From Survivor區域,To Survivor【保留空間】爲空。

GC進行時,Eden區所有存活的對象都被複制到To Survivor區,而From Survivor區中,仍存活的對象會根據它們的年齡值決定去向,年齡值達到年齡閾值(默認15是因爲對象頭中年齡戰4bit,新生代每熬過一次垃圾回收,年齡+1),則移到老年代,沒有達到則複製到To Survivor。

老年代採用標記/清除算法標記/整理算法

由於老年代存活率高,沒有額外空間給他做擔保,必須使用這兩種算法。

枚舉根節點算法

GC Roots 被虛擬機用來判斷對象是否存活

可作爲GC Roos的節點主要是在一些全局引用【如常量或靜態屬性】、執行上下文【如棧幀中本地變量表】中。那麼如何在這麼多全局變量和本地變量表找到【枚舉】根節點將是個問題。

可達性分析算法需考慮

1.如果方法區幾百兆,一個個檢查裏面的引用,將耗費大量資源。

2.在分析時,需保證這個對象引用關係不再變化,否則結果將不準確。【因此GC進行時需停掉其它所有java執行線程(Sun把這種行爲稱爲‘Stop the World’),即使是號稱幾乎不會停頓的CMS收集器,枚舉根節點時也需停掉線程】

解決辦法:實際上當系統停下來後JVM不需要一個個檢查引用,而是通過OopMap數據結構【HotSpot的叫法】來標記對象引用。

虛擬機先得知哪些地方存放對象的引用,在類加載完時。HotSpot把對象內什麼偏移量什麼類型的數據算出來,在jit編譯過程中,也會在特定位置記錄下棧和寄存器哪些位置是引用,這樣GC在掃描時就可以知道這些信息。【目前主流JVM使用準確式GC】

OopMap可以幫助HotSpot快速且準確完成GC Roots枚舉以及確定相關信息。但是也存在一個問題,可能導致引用關係變化。

這個時候有個safepoint(安全點)的概念。

HotSpot中GC不是在任意位置都可以進入,而只能在safepoint處進入。 GC時對一個Java線程來說,它要麼處在safepoint,要麼不在safepoint。

safepoint不能太少,否則GC等待的時間會很久

safepoint不能太多,否則將增加運行GC的負擔

安全點主要存放的位置

1:循環的末尾 
2:方法臨返回前/調用方法的call指令後 
3:可能拋異常的位置

GC回收器解讀

目前主流的HotSpot VM支持多種虛擬機,這些虛擬機也體現了GC的發展歷程,從單線程GC(由於早起機器單核處理)到多線程GC,分代GC到G1 GC。

本文主要從以下幾個方面介紹GC收集器:

  • 各種GC的特點
  • GC匹配和參數使用
  • GC日誌格式
  • 常用的GC參數總結

各種GC的特點

HotSpot中採用分代GC,從早期的單線程串行Garbage Collector到後面的多線程並行Garbage Collecot,衍生出了很多款Collector。

其中負責收集年輕代:

  1. Serial:單線程串行收集器,使用複製算法回收年輕代
  2. ParNew:多線程並行收集器,使用複製算法回收年輕代
  3. Parallel Scavenge:多線程並行收集器,使用複製算法回收年輕代,注重系統運行的吞吐量

其中負責收集老年代:

  1. Serial Old:類似Serial,單線程串行收集器,使用標記整理算法回收老年代
  2. CMS:併發標記整理的收集器,使用標記清除算法回收老年代,主要是爲了減少停頓的時間,降低延遲而生
  3. Parallel Old:類似ParNew和Parallel Scavenge,使用標記整理算法回收老年代

低延遲方案應用趨勢:CMS -> G1 -> ZGC

回收方式的選擇

jvm有client和server兩種模式,這兩種模式的gc默認方式是不同的:

  1. client模式下,新生代選擇的是串行gc,舊生代選擇的是串行gc
  2. server模式下,新生代選擇的是並行回收gc,舊生代選擇的是並行gc
  3. 一般來說我們系統應用選擇有兩種方式:吞吐量優先和暫停時間優先,對於吞吐量優先的採用server默認的並行gc方式,對於暫停時間優先的選用併發gc(CMS)方式。

GC的選擇
官方推薦,需要根據應用的實際情況進行選擇。在選擇之前必須要對應用的堆大小、收集頻率進行估算。

使用SerialGC的場景:
1、如果應用的堆大小在100MB以內。
2、如果應用在一個單核單線程的服務器上面,並且對應用暫停的時間無需求。
使用ParallelGC的場景:
如果需要應用在高峯期有較好的性能,但是對應用停頓時間無高要求(比如:停頓1s甚至更長)。
使用G1、CMS場景:
1、對應用的延遲有很高的要求。
2、如果內存大於6G請使用G1。

什麼時候會發生 Full GC ?

full gc是對新生代,舊生代,以及持久代的統一回收,由於是對整個空間的回收,因此比較慢,系統中應當儘量減少full gc的次數。

  1. Tenured空間不足:java.lang.OutOfMemoryError:Javaheapspace
  2. Perm空間不足:java.lang.OutOfMemoryError:PermGenspace
  3. CMS GC時出現 promotion failed 和 concurrent mode failure
  4. Tenured剩餘空間小於MinorGC晉升的平均大小(悲觀策略)
  5. 主動觸發FullGC:System.gc(),jmap-histo:live[pid]

如何查看運行程序的GC情況

jstat命令命令格式:

jstat [Options] vmid [interval] [count]

參數說明:

Options,選項,我們一般使用 -gcutil 查看gc情況
vmid,VM的進程號,即當前運行的java進程號
interval,間隔時間,單位爲秒或者毫秒
count,打印次數,如果缺省則打印無數次

示例

通常運行命令如下:   jstat -gc 30996 3000

即:每3秒一次顯示進程號爲30996的java進程的GC情況或使用命令:jstat -gcutil 30996 3000

結果說明

顯示內容說明如下(部分結果是通過其他其他參數顯示的,暫不說明):

  1.          S0C:年輕代中第一個survivor(倖存區)的容量 (字節) 
  2.          S1C:年輕代中第二個survivor(倖存區)的容量 (字節) 
  3.          S0U:年輕代中第一個survivor(倖存區)目前已使用空間 (字節) 
  4.          S1U:年輕代中第二個survivor(倖存區)目前已使用空間 (字節) 
  5.          EC:年輕代中Eden(伊甸園)的容量 (字節) 
  6.          EU:年輕代中Eden(伊甸園)目前已使用空間 (字節) 
  7.          OC:Old代的容量 (字節) 
  8.          OU:Old代目前已使用空間 (字節) 
  9.          PC:Perm(持久代)的容量 (字節) 
  10.          PU:Perm(持久代)目前已使用空間 (字節) 
  11.          YGC:從應用程序啓動到採樣時年輕代中gc次數 
  12.          YGCT:從應用程序啓動到採樣時年輕代中gc所用時間(s) 
  13.          FGC:從應用程序啓動到採樣時old代(全gc)gc次數 
  14.          FGCT:從應用程序啓動到採樣時old代(全gc)gc所用時間(s) 
  15.          GCT:從應用程序啓動到採樣時gc用的總時間(s) 
  16.          NGCMN:年輕代(young)中初始化(最小)的大小 (字節) 
  17.          NGCMX:年輕代(young)的最大容量 (字節) 
  18.          NGC:年輕代(young)中當前的容量 (字節) 
  19.          OGCMN:old代中初始化(最小)的大小 (字節) 
  20.          OGCMX:old代的最大容量 (字節) 
  21.          OGC:old代當前新生成的容量 (字節) 
  22.          PGCMN:perm代中初始化(最小)的大小 (字節) 
  23.          PGCMX:perm代的最大容量 (字節)   
  24.          PGC:perm代當前新生成的容量 (字節) 
  25.          S0:年輕代中第一個survivor(倖存區)已使用的佔當前容量百分比 
  26.          S1:年輕代中第二個survivor(倖存區)已使用的佔當前容量百分比 
  27.          E:年輕代中Eden(伊甸園)已使用的佔當前容量百分比 
  28.          O:old代已使用的佔當前容量百分比 
  29.          P:perm代已使用的佔當前容量百分比 
  30.          S0CMX:年輕代中第一個survivor(倖存區)的最大容量 (字節) 
  31.          S1CMX :年輕代中第二個survivor(倖存區)的最大容量 (字節) 
  32.          ECMX:年輕代中Eden(伊甸園)的最大容量 (字節) 
  33.          DSS:當前需要survivor(倖存區)的容量 (字節)(Eden區已滿) 
  34.          TT: 持有次數限制 
  35.          MTT : 最大持有次數限制 

理解 GC 併發收集的思路

• 併發GC根本上要跟應用玩追趕遊戲:應用一邊在分配,GC一邊在收集,如果GC收集的速度能跟得上 應用分配的速度,那就一切都很完美;一旦GC開始跟不上了,垃圾就會漸漸堆積起來,最終到可用空 間徹底耗盡的時候,應用的分配請求就只能暫時等一等了,等GC追趕上來。

1.Serial收集器

Serial收集器是一款非常古老的收集器,它使用單線程串行方式回收年輕代,會產生STW。

Note:
STW即Stop The World,即停止所有用戶線程,只有GC線程在運行。

每次進行GC時,首先停頓所有的用戶線程,然後只有一個GC線程回收年輕代中的死亡對象。在Java Client模式中,默認使用Serial,因爲Client模式主要針對桌面應用,一般內存較小,在百M範圍內,使用單線程收集Serial效率非常高,可以帶來很少時間的停頓,用戶體檢非常好。

2.ParNew收集器

在早期,只有單線程收集器時,年輕代別無選擇。後續又演變成多線程GC年輕代,便衍生出ParNew這款並行收集器,它的並行實現主要是在GC期間使用多線程回收年輕代。

這款並行收集器在GC期間,也需要STW。一般多數用於Server端的年輕代GC。

3.Parallel Scavenge收集器

顧名思義,這個款年輕代收集器也是並行收集器,和ParNew的功能差不多,同樣適用複製算法。但是它更注重系統運行的吞吐量。這裏說的吞吐量,指的是CPU用於運行應用程序的時間和CPU總時間的佔比,吞吐量 = 代碼運行時間 / (代碼運行時間 + 垃圾收集時間)。但是它的來源比較奇葩,沒有遵循GC框架,導致和CMS不能兼容。關於這點可以參考:

ParNew 和 PSYoungGen 和 DefNew 是一個東西麼?

4.Serial Old收集器

該收集器和Serial收集器的功能一樣,都是單線程串行收集器,GC期間也會STW。但是它用於收集老年代且使用了標記整理算法,這兩點它和Serial收集器不一樣。主要也是應用在Client模式下的桌面應用中。

5.Parallel Old收集器

Parallel Old和ParNew和Parallel Scavenge類似,是一款老年代的多線程並行收集器。一般只配合Parallel Scavenge使用。

6.CMS收集器

CMS(Concurrent Mark Sweep 併發標記清理)收集器是日常應用中最常被使用的收集器。它主要是爲了減少停頓的時間,降低延遲而生,多應用對實時性要求比較的應用場景,如:互聯網應用。

它主要分爲四個過程:

  1. 初始標記:這階段將標記不可達對象,標記階段將暫停所有用戶線程,造成STW。但是這階段只是標記出GC Roots,停頓時間相對較短。
  2. 併發標記:這階段GC線程將會和用戶線程同時運行,將從初始階段標記出的GC Roots出發標記老年代所有對象
  3. 重新標記:這階段將暫停所用用戶線程,造成STW。但是同樣相對較短,主要是爲了重新標記出在併發階段發生引用變化的對象,因爲併發標記階段是和用戶線程併發運行,可能會造成對象的引用關係發生變化。
  4. 併發清除:這是最後一個階段,也是和用戶線程同時運行的。將併發的清理掉被標記的死亡對象。

其中初始標記和重新標記仍然會STW暫停用戶線程,但是這兩個過程的停頓時間相對於併發標記和併發清除而言相對較短,而併發標記和併發清除階段GC線程則可以和用戶線程併發運行。

由於CMS收集器同樣使用標記清除算法,所以存在內存碎片問題,從而可能造成大對象無法分配發生提前GC。所以CMS收集器又提供了參數控制其進行內存碎片整理,默認是開啓狀態,這個過程是非常長的。

GC匹配和參數使用

從以上內容介紹,可以看出分代GC分爲很多種,隨着演化過程,每種都有各自的應用場景。從其收集特點上可以分爲三類:

  1. 單線程串行收集
  2. 多線程併發串行收集
  3. 多階段並行收集

雖然這些分代收集器種類繁多,但是他們之間有相互匹配,並非任意使用。配對的使用情況以及參數見下表:

young old 參數
Serial Serial old -XX:+UseSerialGC
ParNew Serial old -XX:+UseParNewGC
Parallel Scavenge Serial old -XX:+UseParallelGC
Parallel Scavenge Parallel Old -XX:+UseParallelOldGC
ParNew CMS + Serial Old -XX:+UseConcMarkSweepGC
Serial CMS -XX:+UseConcMarkSweepGC -XX:-UseParNewGC

注意:利用-XX:+UseG1GC -XX:+UseStringDeduplication來減少重複字符串導致的內存浪費

Gc日誌參數詳解

  • -verbose.gc開關可顯示GC的操作內容。打開它,可以顯示最忙和最空閒收集行爲發生的時間、收集前後的內存大小、收集需要的時間等。
  • 打開-xx:+printGCdetails開關,可以詳細瞭解GC中的變化。
  • 打開-XX:+PrintGCTimeStamps開關,可以瞭解這些垃圾收集發生的時間,自JVM啓動以後以秒計量。
  • 最後,通過-xx:+PrintHeapAtGC開關了解堆的更詳細的信息。
  • 爲了瞭解新域的情況,可以通過-XX:+PrintTenuringDistribution開關了解獲得使用期的對象權。
  • -Xloggc:$CATALINA_BASE/logs/gc.log gc 配置gc日誌產生的路徑
  • -XX:+PrintGCApplicationStoppedTime 輸出GC造成應用暫停的時間
  • -XX:+PrintGCDateStamps GC發生的時間信息
-Xmx${HEAPSIZE}m -Xms${HEAPSIZE}m -XX:+UseG1GC -XX:MaxGCPauseMillis=80 -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:${LOGS_DIR}

常用JVM參數

  1. -Xmn    新生代大小。通常爲 Xmx 的 1/3 或 1/4。新生代 = Eden + 2 個 Survivor 空間。實際可用空間爲 = Eden + 1 個 Survivor,即 90% 
  2. -Xss    JDK1.5+ 每個線程堆棧大小爲 1M,一般來說如果棧不是很深的話, 1M 是絕對夠用了的。
  3. -XX:NewRatio    新生代與老年代的比例,如 –XX:NewRatio=2,則新生代佔整個堆空間的1/3,老年代佔2/3
  4. -XX:SurvivorRatio    新生代中 Eden 與 Survivor 的比值。默認值爲 8。即 Eden 佔新生代空間的 8/10,另外兩個 Survivor 各佔 1/10 
  5. Xms 是指設定程序啓動時佔用內存大小。一般來講,大點,程序會啓動的快一點,但是也可能會導致機器暫時間變慢。
  6. Xmx 是指設定程序運行期間最大可佔用的內存大小。如果程序運行需要佔用更多的內存,超出了這個設置值,就會拋出OutOfMemory異常。
  7. Xss 是指設定每個線程的堆棧大小。這個就要依據你的程序,看一個線程大約需要佔用多少內存,可能會有多少線程同時運行等。
  8. 以上三個參數的設置都是默認以Byte爲單位的,也可以在數字後面添加[k/K]或者[m/M]來表示KB或者MB。而且,超過機器本身的內存大小也是不可以的,否則就等着機器變慢而不是程序變慢了。
  • -Xms 爲jvm啓動時分配的內存,比如-Xms200m,表示分配200M,一般與-Xmx設置一樣大
  • -Xmx 爲jvm運行過程中分配的最大內存,比如-Xms500m,表示jvm進程最多隻能夠佔用500M內存,
  • -Xss 爲jvm啓動的每個線程分配的內存大小,默認JDK1.4中是256K,JDK1.5+中是1M
Total Memory -Xms -Xmx -Xss Spare Memory JDK Thread Count
1024M 256M 256M 256K 768M 1.4 3072
1024M 256M 256M 256K 768M 1.5 768

此外還有一個持久代: 用於存放靜態文件,如今Java類、方法等。持久代對垃圾回收沒有顯著影響,但是有些應用可能動態生成或者調用一些class,例如Hibernate等,在這種時候需要設置一個比較大的持久代空間來存放這些運行過程中新增的類。持久代大小通過 -XX:MaxPermSize=<N> 進行設置。

並行收集器相關參數

-XX:+UseParallelGC Full GC採用parallel MSC
(此項待驗證)
 

選擇垃圾收集器爲並行收集器.此配置僅對年輕代有效.即上述配置下,年輕代使用併發收集,而年老代仍舊使用串行收集.(此項待驗證)

-XX:+UseParNewGC 設置年輕代爲並行收集   可與CMS收集同時使用
JDK5.0以上,JVM會根據系統配置自行設置,所以無需再設置此值
-XX:ParallelGCThreads 並行收集器的線程數   此值最好配置與處理器數目相等 同樣適用於CMS,默認與CPU核數相同
-XX:+UseParallelOldGC 年老代垃圾收集方式爲並行收集(Parallel Compacting)   這個是JAVA 6出現的參數選項
-XX:MaxGCPauseMillis 每次年輕代垃圾回收的最長時間(最大暫停時間)   如果無法滿足此時間,JVM會自動調整年輕代大小,以滿足此值.
-XX:+UseAdaptiveSizePolicy 自動選擇年輕代區大小和相應的Survivor區比例   設置此選項後,並行收集器會自動選擇年輕代區大小和相應的Survivor區比例,以達到目標系統規定的最低相應時間或者收集頻率等,此值建議使用並行收集器時,一直打開.
-XX:GCTimeRatio 設置垃圾回收時間佔程序運行時間的百分比   公式爲1/(1+n)
-XX:+ScavengeBeforeFullGC Full GC前調用YGC true Do young generation GC prior to a full GC. (Introduced in 1.4.1.)

輔助信息

-XX:+PrintGC    

輸出形式:

[GC 118250K->113543K(130112K), 0.0094143 secs]
[Full GC 121376K->10414K(130112K), 0.0650971 secs]

-XX:+PrintGCDetails    

輸出形式:[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs]
[GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K), 0.0436268 secs]

-XX:+PrintGCTimeStamps      
-XX:+PrintGC:PrintGCTimeStamps     可與-XX:+PrintGC -XX:+PrintGCDetails混合使用
輸出形式:11.851: [GC 98328K->93620K(130112K), 0.0082960 secs]
-XX:+PrintGCApplicationStoppedTime 打印垃圾回收期間程序暫停的時間.可與上面混合使用   輸出形式:Total time for which application threads were stopped: 0.0468229 seconds
-XX:+PrintGCApplicationConcurrentTime 打印每次垃圾回收前,程序未中斷的執行時間.可與上面混合使用   輸出形式:Application time: 0.5291524 seconds
-XX:+PrintHeapAtGC 打印GC前後的詳細堆棧信息    
-Xloggc:filename 把相關日誌信息記錄到文件以便分析.
與上面幾個配合使用
   

-XX:+PrintClassHistogram

garbage collects before printing the histogram.    
-XX:+PrintTLAB 查看TLAB空間的使用情況    
XX:+PrintTenuringDistribution 查看每次minor GC後新的存活週期的閾值  

Desired survivor size 1048576 bytes, new threshold 7 (max 15)
new threshold 7即標識新的存活週期的閾值爲7。

有的虛擬機並沒有持久代,JAVA8 開始持久代也已經被徹底刪除了,取代它的是另一個內存區域也被稱爲 元空間。、

示例

下面對如下的參數進行分析:

JAVA_OPTS="-server -Xms2000m -Xmx2000m -Xmn800m -XX:PermSize=64m -XX:MaxPermSize=256m -XX:SurvivorRatio=4
-verbose:gc -Xloggc:$CATALINA_HOME/logs/gc.log 
-Djava.awt.headless=true 
-XX:+PrintGCTimeStamps -XX:+PrintGCDetails 
-Dsun.rmi.dgc.server.gcInterval=600000 -Dsun.rmi.dgc.client.gcInterval=600000
-XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=15"
  • -Xms2000m -Xmx2000m -Xmn800m -XX:PermSize=64m -XX:MaxPermSize=256m

Xms,即爲jvm啓動時得JVM初始堆大小,Xmx爲jvm的最大堆大小,xmn爲新生代的大小,permsize爲永久代的初始大小,MaxPermSize爲永久代的最大空間。

  • -XX:SurvivorRatio=4

SurvivorRatio爲新生代空間中的Eden區和救助空間Survivor區的大小比值,默認是32,也就是說Eden區是 Survivor區的32倍大小,要注意Survivo是有兩個區的,因此Surivivor其實佔整個young genertation的1/34。調小這個參數將增大survivor區,讓對象儘量在survitor區呆長一點,減少進入年老代的對象。去掉救助空間的想法是讓大部分不能馬上回收的數據儘快進入年老代,加快年老代的回收頻率,減少年老代暴漲的可能性,這個是通過將-XX:SurvivorRatio 設置成比較大的值(比如65536)來做到。

  • -verbose:gc -Xloggc:$CATALINA_HOME/logs/gc.log

將虛擬機每次垃圾回收的信息寫到日誌文件中,文件名由file指定,文件格式是平文件,內容和-verbose:gc輸出內容相同。

  • -Djava.awt.headless=true

Headless模式是系統的一種配置模式。在該模式下,系統缺少了顯示設備、鍵盤或鼠標。

  • -XX:+PrintGCTimeStamps -XX:+PrintGCDetails

設置gc日誌的格式

  • -Dsun.rmi.dgc.server.gcInterval=600000 -Dsun.rmi.dgc.client.gcInterval=600000

指定rmi調用時gc的時間間隔

  • -XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=15

採用併發gc方式,經過15次minor gc 後進入年老代

Xms 是指設定程序啓動時佔用內存大小。一般來講,大點,程序會啓動的快一點,但是也可能會導致機器暫時間變慢。Xmx 是指設定程序運行期間最大可佔用的內存大小。如果程序運行需要佔用更多的內存,超出了這個設置值,就會拋出OutOfMemory異常。

Xss 是指設定每個線程的堆棧大小。這個就要依據你的程序,看一個線程大約需要佔用多少內存,可能會有多少線程同時運行等。

以上三個參數的設置都是默認以Byte爲單位的,也可以在數字後面添加[k/K]或者[m/M]來表示KB或者MB。而且,超過機器本身的內存大小也是不可以的,否則就等着機器變慢而不是程序變慢了。
 

掃一掃加入大數據公衆號,瞭解更多大數據技術,還有免費資料等你哦

掃一掃加入大數據公衆號,瞭解更多大數據技術,還有免費資料等你哦

掃一掃加入大數據公衆號,瞭解更多大數據技術,還有免費資料等你哦


 

 

 

 

 

 

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