導航:首頁 > 萬維百科 > cmsgclog

cmsgclog

發布時間:2020-09-22 00:50:01

1、為什麼cms GC時出現Concurrent Mode Failure

出現Concurrent ModeFailure現象時,解決辦法就是要讓年老代留有足夠的空間,以保證新對象空間的分配。另外在JVM BUG中有提到,JDK1.5_09版本之前,JVM參數-XX:是無效的,我這里應用環境的版本是JDK1.5_08,從gc日誌來看是可以生效的。
GC時還有一個常見的錯誤PromotionFailed,解決辦法類似,也是調整年輕代和年老代的比例,還有CMSGC的時機。

2、如何查看GC 及jvm配置?

java雖然是自動回收內存,但是應用程序,尤其伺服器程序最好根據業務情況指明內存分配限制。否則可能導致應用程序宕掉。

舉例說明含義:
-Xms128m
表示JVM Heap(堆內存)最小尺寸128MB,初始分配
-Xmx512m
表示JVM Heap(堆內存)最大允許的尺寸256MB,按需分配。

說明:如果-Xmx不指定或者指定偏小,應用可能會導致java.lang.OutOfMemory錯誤,此錯誤來自JVM不是Throwable的,無法用try...catch捕捉。

PermSize和MaxPermSize指明虛擬機為java永久生成對象(Permanate generation)如,class對象、方法對象這些可反射(reflective)對象分配內存限制,這些內存不包括在Heap(堆內存)區之中。

-XX:PermSize=64MB 最小尺寸,初始分配
-XX:MaxPermSize=256MB 最大允許分配尺寸,按需分配
過小會導致:java.lang.OutOfMemoryError: PermGen space

MaxPermSize預設值和-server -client選項相關。
-server選項下默認MaxPermSize為64m
-client選項下默認MaxPermSize為32m

經驗:
1、慎用最小限制選項Xms,PermSize已節約系統資源。

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

近期研究對jvm的內存使用情況進行監控,因此對觀察虛擬機的內存使用方法做了一些收集,對jvm的參數設置了解了一下:

幾個基本概念:

PermGen space:全稱是Permanent Generation space,即永久代。就是說是永久保存的區域,用於存放Class和Meta信息,Class在被Load的時候被放入該區域,GC(Garbage Collection)應該不會對PermGen space進行清理,所以如果你的APP會LOAD很多CLASS的話,就很可能出現PermGen space錯誤。
Heap space:存放Instance。Java Heap分為3個區,Young即新生代,Old即老生代和Permanent。Young保存剛實例化的對象。當該區被填滿時,GC會將對象移到Old區。Permanent區則負責保存反射對象。

幾個參數設置的意義:

xms/xmx:定義YOUNG+OLD段的總尺寸,ms為JVM啟動時YOUNG+OLD的內存大小;mx為最大可佔用的YOUNG+OLD內存大小。在用戶生產環境上一般將這兩個值設為相同,以減少運行期間系統在內存申請上所花的開銷。
NewSize/MaxNewSize:定義YOUNG段的尺寸,NewSize為JVM啟動時YOUNG的內存大小;MaxNewSize為最大可佔用的YOUNG內存大小。在用戶生產環境上一般將這兩個值設為相同,以減少運行期間系統在內存申請上所花的開銷。
PermSize/MaxPermSize:定義Perm段的尺寸,PermSize為JVM啟動時Perm的內存大小;MaxPermSize為最大可佔用的Perm內存大小。在用戶生產環境上一般將這兩個值設為相同,以減少運行期間系統在內存申請上所花的開銷。
SurvivorRatio:設置YOUNG代中Survivor空間和Eden空間的比例

申請一塊內存的過程:

A. JVM會試圖為相關Java對象在Eden中初始化一塊內存區域
B. 當Eden空間足夠時,內存申請結束。否則到下一步
C. JVM試圖釋放在Eden中所有不活躍的對象(這屬於1或更高級的垃圾回收);釋放後若Eden空間仍然不足以放入新對象,則試圖將部分Eden中活躍對象放入Survivor區/OLD區
D. Survivor區被用來作為Eden及OLD的中間交換區域,當OLD區空間足夠時,Survivor區的對象會被移到Old區,否則會被保留在Survivor區
E. 當OLD區空間不夠時,JVM會在OLD區進行完全的垃圾收集(0級)
F. 完全垃圾收集後,若Survivor及OLD區仍然無法存放從Eden復制過來的部分對象,導致JVM無法在Eden區為新對象創建內存區域,則出現」out of memory錯誤」

我們的一種resin伺服器的jvm參數設置:

「-Xmx2000M -Xms2000M -Xmn500M -XX:PermSize=250M -XX:MaxPermSize=250M -Xss256K -XX:+DisableExplicitGC -XX:SurvivorRatio=1 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+CMSClassUnloadingEnabled -XX:LargePageSizeInBytes=128M -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:=60 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+PrintClassHistogram -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -Xloggc:log/gc.log」

是一種典型的響應時間優先型的配置。

Java中有四種不同的回收演算法,對應的啟動參數為
–XX:+UseSerialGC
–XX:+UseParallelGC
–XX:+UseParallelOldGC
–XX:+UseConcMarkSweepGC

1. Serial Collector
大部分平台或者強制 java -client 默認會使用這種。
young generation演算法 = serial
old generation演算法 = serial (mark-sweep-compact)
這種方法的缺點很明顯,stop-the-world, 速度慢。伺服器應用不推薦使用。

2. Parallel Collector
在linux x64上默認是這種,其他平台要加 java -server 參數才會默認選用這種。
young = parallel,多個thread同時copy
old = mark-sweep-compact = 1
優點:新生代回收更快。因為系統大部分時間做的gc都是新生代的,這樣提高了throughput(cpu用於非gc時間)
缺點:當運行在8G/16G server上old generation live object太多時候pause time過長

3. Parallel Compact Collector (ParallelOld)
young = parallel = 2
old = parallel,分成多個獨立的單元,如果單元中live object少則回收,多則跳過
優點:old old generation上性能較 parallel 方式有提高
缺點:大部分server系統old generation內存佔用會達到60%-80%, 沒有那麼多理想的單元live object很少方便迅速回收,同時compact方面開銷比起parallel並沒明顯減少。

4. Concurent Mark-Sweep(CMS) Collector
young generation = parallel collector = 2
old = cms
同時不做 compact 操作。
優點:pause time會降低, pause敏感但CPU有空閑的場景需要建議使用策略4.
缺點:cpu佔用過多,cpu密集型伺服器不適合。另外碎片太多,每個object的存儲都要通過鏈表連續跳n個地方,空間浪費問題也會增大。

內存監控的方法:

1. jmap -heap pid
查看java 堆(heap)使用情況

using thread-local object allocation.
Parallel GC with 4 thread(s) //GC 方式

Heap Configuration: //堆內存初始化配置
MinHeapFreeRatio=40 //對應jvm啟動參數-XX:MinHeapFreeRatio設置JVM堆最小空閑比率(default 40)
MaxHeapFreeRatio=70 //對應jvm啟動參數 -XX:MaxHeapFreeRatio設置JVM堆最大空閑比率(default 70)
MaxHeapSize=512.0MB //對應jvm啟動參數-XX:MaxHeapSize=設置JVM堆的最大大小
NewSize = 1.0MB //對應jvm啟動參數-XX:NewSize=設置JVM堆的『新生代』的默認大小
MaxNewSize =4095MB //對應jvm啟動參數-XX:MaxNewSize=設置JVM堆的『新生代』的最大大小
OldSize = 4.0MB //對應jvm啟動參數-XX:OldSize=<value>:設置JVM堆的『老生代』的大小
NewRatio = 8 //對應jvm啟動參數-XX:NewRatio=:『新生代』和『老生代』的大小比率
SurvivorRatio = 8 //對應jvm啟動參數-XX:SurvivorRatio=設置年輕代中Eden區與Survivor區的大小比值
PermSize= 16.0MB //對應jvm啟動參數-XX:PermSize=<value>:設置JVM堆的『永生代』的初始大小
MaxPermSize=64.0MB //對應jvm啟動參數-XX:MaxPermSize=<value>:設置JVM堆的『永生代』的最大大小

Heap Usage: //堆內存分步
PS Young Generation
Eden Space: //Eden區內存分布
capacity = 20381696 (19.4375MB) //Eden區總容量
used = 20370032 (19.426376342773438MB) //Eden區已使用
free = 11664 (0.0111236572265625MB) //Eden區剩餘容量
99.94277218147106% used //Eden區使用比率
From Space: //其中一個Survivor區的內存分布
capacity = 8519680 (8.125MB)
used = 32768 (0.03125MB)
free = 8486912 (8.09375MB)
0.38461538461538464% used
To Space: //另一個Survivor區的內存分布
capacity = 9306112 (8.875MB)
used = 0 (0.0MB)
free = 9306112 (8.875MB)
0.0% used
PS Old Generation //當前的Old區內存分布
capacity = 366280704 (349.3125MB)
used = 322179848 (307.25464630126953MB)
free = 44100856 (42.05785369873047MB)
87.95982001825573% used
PS Perm Generation //當前的 「永生代」 內存分布
capacity = 32243712 (30.75MB)
used = 28918584 (27.57891082763672MB)
free = 3325128 (3.1710891723632812MB)
89.68751488662348% used

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

jps
-q只輸出進程ID,而不輸出類的短名稱
-m用於輸出傳遞給Java進程(主函數)的參數
-l完整路徑
-v顯示傳遞給jvm的參數

3、cms gc過程中哪幾個階段暫停應用程序

問題解決:中間調整過幾次,先搞了幾台機器做了驗證,後來逐步推廣的。
1、調大heap區,由原來的4g,調整到5g,young區的大小不變,還是2g,這時候old區就由2g變為3g了(這樣保證old區有足夠的空間);
2、設置-XX:UseCMSInitiatingOccupancyOnly,其實這個不關這個問題,只是發現半夜CMS進行的有點頻繁,就禁止掉了悲觀策略;
3、設置CMS區回收的比例,從80%調整到75%,讓old區盡早的進行,有足夠的空間剩餘;

為什麼要有GC(垃圾回收)?

JVM通過GC來回收堆和方法區中的內存,GC的基本原理就是找到程序中不再被使用的對象,然後回收掉這些對象佔用的內存。

4、如何查看 java gc 類型

Java中的GC有哪幾種類型?

參數

描述

UseSerialGC

虛擬機運行在Client模式的默認值,打開此開關參數後,
使用Serial+Serial Old收集器組合進行垃圾收集。

UseParNewGC

打開此開關參數後,使用ParNew+Serial Old收集器組合進行垃圾收集。

UseConcMarkSweepGC

打開此開關參數後,使用ParNew+CMS+Serial Old收集器組合進行垃圾收集。Serial Old作為CMS收集器出現Concurrent Mode Failure的備用垃圾收集器。

UseParallelGC

虛擬機運行在Server模式的默認值,打開此開關參數後,使用Parallel Scavenge+Serial Old收集器組合進行垃圾收集。

UseParallelOldGC

打開此開關參數後,使用Parallel Scavenge+Parallel Old收集器組合進行垃圾收集。

在Java程序啟動完成後,通過jps觀察進程來查詢到當前運行的java進程,使用
jinfo –flag UseSerialGC 進程

的方式可以定位其使用的gc策略,因為這些參數都是boolean型的常量,如果使用該種gc策略會出現+號,否則-號。

使用-XX:+上述GC策略可以開啟對應的GC策略。

GC日誌查看
可以通過在java命令種加入參數來指定對應的gc類型,列印gc日誌信息並輸出至文件等策略。

GC的日誌是以替換的方式(>)寫入的,而不是追加(>>),如果下次寫入到同一個文件中的話,以前的GC內容會被清空。

對應的參數列表
-XX:+PrintGC 輸出GC日誌
-XX:+PrintGCDetails 輸出GC的詳細日誌
-XX:+PrintGCTimeStamps 輸出GC的時間戳(以基準時間的形式)
-XX:+PrintGCDateStamps 輸出GC的時間戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)
-XX:+PrintHeapAtGC 在進行GC的前後列印出堆的信息
-Xloggc:../logs/gc.log 日誌文件的輸出路徑

這里使用如下的參數來進行日誌的列印:
-XX:+PrintGCDateStamps -XX:+PrintGCDetails -Xloggc:./gclogs

對於新生代回收的一行日誌,其基本內容如下:
2014-07-18T16:02:17.606+0800: 611.633: [GC 611.633: [DefNew: 843458K->2K(948864K), 0.0059180 secs] 2186589K->1343132K(3057292K), 0.0059490 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

其含義大概如下:
2014-07-18T16:02:17.606+0800(當前時間戳): 611.633(時間戳): [GC(表示Young GC) 611.633: [DefNew(單線程Serial年輕代GC): 843458K(年輕代垃圾回收前的大小)->2K(年輕代回收後的大小)(948864K(年輕代總大小)), 0.0059180 secs(本次回收的時間)] 2186589K(整個堆回收前的大小)->1343132K(整個堆回收後的大小)(3057292K(堆總大小)), 0.0059490 secs(回收時間)] [Times: user=0.00(用戶耗時) sys=0.00(系統耗時), real=0.00 secs(實際耗時)]

老年代回收的日誌如下:
2014-07-18T16:19:16.794+0800: 1630.821: [GC 1630.821: [DefNew: 1005567K->111679K(1005568K), 0.9152360 secs]1631.736: [Tenured:
2573912K->1340650K(2574068K), 1.8511050 secs] 3122548K->1340650K(3579636K), [Perm : 17882K->17882K(21248K)], 2.7854350 secs] [Times: user=2.57 sys=0.22, real=2.79 secs]

gc日誌中的最後貌似是系統運行完成前的快照:
Heap
def new generation total 1005568K, used 111158K [0x00000006fae00000, 0x000000073f110000, 0x0000000750350000)
eden space 893888K, 12% used [0x00000006fae00000, 0x0000000701710e90, 0x00000007316f0000)
from space 111680K, 3% used [0x0000000738400000, 0x000000073877c9b0, 0x000000073f110000)
to space 111680K, 0% used [0x00000007316f0000, 0x00000007316f0000, 0x0000000738400000)
tenured generation total 2234420K, used 1347671K [0x0000000750350000, 0x00000007d895d000, 0x00000007fae00000)
the space 2234420K, 60% used [0x0000000750350000, 0x00000007a2765cb8, 0x00000007a2765e00, 0x00000007d895d000)
compacting perm gen total 21248K, used 17994K [0x00000007fae00000, 0x00000007fc2c0000, 0x0000000800000000)
the space 21248K, 84% used [0x00000007fae00000, 0x00000007fbf92a50, 0x00000007fbf92c00, 0x00000007fc2c0000)
No shared spaces configured.

GC日誌的離線分析
可以使用一些離線的工具來對GC日誌進行分析,比如sun的gchisto( https://java.net/projects/gchisto),gcviewer( https://github.com/chewiebug/GCViewer ),這些都是開源的工具,用戶可以直接通過版本控制工具下載其源碼,進行離線分析。

下面就已gcviewer為例,簡要分析一下gc日誌的離線分析,gcviewer源代碼工程是maven結構的,可以直接用maven進行package,這里編譯的是1.34版本,本版本的快照已經上傳至附件中。

需要說明的是,gcviewer支持多種參數生成的gc日誌,直接通過java –jar的方式運行,載入生成的gc日誌即可:

5、Tomcat GC Log,怎麼看GC有沒有問題,每個參數都是什麼意思。伺服器突然掛了,log沒有里任何異常信息!

98277.469: [GC 98277.469: [ParNew: 724452K->49066K(853376K), 0.0701960 secs] 841423K->166244K(1877376K), 0.0707240 secs] [Times: user=0.54 sys=0.00, real=0.07 secs]

ParNew垃圾回收器的類型,這個是處理新生代的,724452K->49066K清理前後的大小。(853376K)總大小,0.0701960 secs 消耗時間
841423K->166244K(1877376K), 0.0707240 secs zhege 可能就是老年代的回收了。也是大小,時間。
[Times: user=0.59 sys=0.00, real=0.08 secs] 這個就不清楚了。第一個猜測可能是服務暫停響應時間吧。

從給出的日誌好像看不出問題啊。

6、java fullgc什麼意思

GC是垃圾回收站。
FULL GC分析和問題定位
a. GC log收集和分析
(1)在JVM啟動參數增加:"-verbose:gc -Xloggc:<file_name> -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
PrintGCTimeStamp只能獲得相對時間,建議使用PrintGCDateStamps獲得full gc 發生的絕對時間
(2)如果採用CMS GC,仔細分析jstat FGC輸出和GC 日誌會發現, CMS的每個並發GC周期則有兩個stop-the-world階段——initial mark與final re-mark,使得CMS的每個並發GC周期總共會更新full GC計數器兩次,initial mark與final re-mark各一次

b. Dump JVM 內存快照
/opt/taobao/java/bin/jmap -mp:format=b,file=mp.bin pid
這里有一個問題是什麼時候進行mp?
一種方法是前面提到的用jstat工具觀察,當OLD區到達比較高的比例如60%,一般會很快觸發一次FULL GC,可以進行一次DUMP,在FULL GC發生以後再DUMP一次,這樣比較就可以發現到底是哪些對象導致不停的FULL GC
另外一種方法是通過配置JVM參數
-XX:+HeapDumpBeforeFullGC -XX:+HeapDumpAfterFullGC分別用於指定在full GC之前與之後生成heap mp

c. 利用MAT((Memory Analyzer Tool)工具分析mp文件
關於MAT具體使用方法網上有很多介紹,這里不做詳細展開,這里需要注意的是:
(1) MAT預設只分析reachable的對象,unreachable的對象(將被收集掉的對象)被忽略,而分析FULL GC頻繁原因時unreachable object也應該同時被重點關注。如果要顯示unreachable的對象細節必須用mat 1.1以上版本並且打開選項「keep unreachable object」
(2) 通常mp文件會好幾個G,無法在windows上直接進行分析,我們可以先把mp文件在linux上進行分析,再把分析好的文件拷貝到windows上,在windows上用MAT打開分析文件。

7、為什麼CMS GC時出現Concurrent Mode Failure

最後總結下,出現Concurrent ModeFailure現象時,解決辦法就是要讓年老代留有足夠的空間,以保證新對象空間的分配。另外在JVM BUG中有提到,JDK1.5_09版本之前,JVM參數-XX:是無效的,我這里應用環境的版本是JDK1.5_08,從gc日誌來看是可以生效的。
GC時還有一個常見的錯誤PromotionFailed,解決辦法類似,也是調整年輕代和年老代的比例,還有CMSGC的時機。

8、如何查看GC 及jvm配置

java雖然是自動回收內存,但是應用程序,尤其伺服器程序最好根據業務情況指明內存分配限制。否則可能導致應用程序宕掉。

舉例說明含義:
-Xms128m
表示JVM Heap(堆內存)最小尺寸128MB,初始分配
-Xmx512m
表示JVM Heap(堆內存)最大允許的尺寸256MB,按需分配。

說明:如果-Xmx不指定或者指定偏小,應用可能會導致java.lang.OutOfMemory錯誤,此錯誤來自JVM不是Throwable的,無法用try...catch捕捉。

PermSize和MaxPermSize指明虛擬機為java永久生成對象(Permanate generation)如,class對象、方法對象這些可反射(reflective)對象分配內存限制,這些內存不包括在Heap(堆內存)區之中。

-XX:PermSize=64MB 最小尺寸,初始分配
-XX:MaxPermSize=256MB 最大允許分配尺寸,按需分配
過小會導致:java.lang.OutOfMemoryError: PermGen space

MaxPermSize預設值和-server -client選項相關。
-server選項下默認MaxPermSize為64m
-client選項下默認MaxPermSize為32m

經驗:
1、慎用最小限制選項Xms,PermSize已節約系統資源。

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

近期研究對jvm的內存使用情況進行監控,因此對觀察虛擬機的內存使用方法做了一些收集,對jvm的參數設置了解了一下:

幾個基本概念:

PermGen space:全稱是Permanent Generation space,即永久代。就是說是永久保存的區域,用於存放Class和Meta信息,Class在被Load的時候被放入該區域,GC(Garbage Collection)應該不會對PermGen space進行清理,所以如果你的APP會LOAD很多CLASS的話,就很可能出現PermGen space錯誤。
Heap space:存放Instance。Java Heap分為3個區,Young即新生代,Old即老生代和Permanent。Young保存剛實例化的對象。當該區被填滿時,GC會將對象移到Old區。Permanent區則負責保存反射對象。

幾個參數設置的意義:

xms/xmx:定義YOUNG+OLD段的總尺寸,ms為JVM啟動時YOUNG+OLD的內存大小;mx為最大可佔用的YOUNG+OLD內存大小。在用戶生產環境上一般將這兩個值設為相同,以減少運行期間系統在內存申請上所花的開銷。
NewSize/MaxNewSize:定義YOUNG段的尺寸,NewSize為JVM啟動時YOUNG的內存大小;MaxNewSize為最大可佔用的YOUNG內存大小。在用戶生產環境上一般將這兩個值設為相同,以減少運行期間系統在內存申請上所花的開銷。
PermSize/MaxPermSize:定義Perm段的尺寸,PermSize為JVM啟動時Perm的內存大小;MaxPermSize為最大可佔用的Perm內存大小。在用戶生產環境上一般將這兩個值設為相同,以減少運行期間系統在內存申請上所花的開銷。
SurvivorRatio:設置YOUNG代中Survivor空間和Eden空間的比例

申請一塊內存的過程:

A. JVM會試圖為相關Java對象在Eden中初始化一塊內存區域
B. 當Eden空間足夠時,內存申請結束。否則到下一步
C. JVM試圖釋放在Eden中所有不活躍的對象(這屬於1或更高級的垃圾回收);釋放後若Eden空間仍然不足以放入新對象,則試圖將部分Eden中活躍對象放入Survivor區/OLD區
D. Survivor區被用來作為Eden及OLD的中間交換區域,當OLD區空間足夠時,Survivor區的對象會被移到Old區,否則會被保留在Survivor區
E. 當OLD區空間不夠時,JVM會在OLD區進行完全的垃圾收集(0級)
F. 完全垃圾收集後,若Survivor及OLD區仍然無法存放從Eden復制過來的部分對象,導致JVM無法在Eden區為新對象創建內存區域,則出現」out of memory錯誤」

我們的一種resin伺服器的jvm參數設置:

「-Xmx2000M -Xms2000M -Xmn500M -XX:PermSize=250M -XX:MaxPermSize=250M -Xss256K -XX:+DisableExplicitGC -XX:SurvivorRatio=1 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+CMSClassUnloadingEnabled -XX:LargePageSizeInBytes=128M -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:=60 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+PrintClassHistogram -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -Xloggc:log/gc.log」

是一種典型的響應時間優先型的配置。

Java中有四種不同的回收演算法,對應的啟動參數為
–XX:+UseSerialGC
–XX:+UseParallelGC
–XX:+UseParallelOldGC
–XX:+UseConcMarkSweepGC

1. Serial Collector
大部分平台或者強制 java -client 默認會使用這種。
young generation演算法 = serial
old generation演算法 = serial (mark-sweep-compact)
這種方法的缺點很明顯,stop-the-world, 速度慢。伺服器應用不推薦使用。

2. Parallel Collector
在linux x64上默認是這種,其他平台要加 java -server 參數才會默認選用這種。
young = parallel,多個thread同時copy
old = mark-sweep-compact = 1
優點:新生代回收更快。因為系統大部分時間做的gc都是新生代的,這樣提高了throughput(cpu用於非gc時間)
缺點:當運行在8G/16G server上old generation live object太多時候pause time過長

3. Parallel Compact Collector (ParallelOld)
young = parallel = 2
old = parallel,分成多個獨立的單元,如果單元中live object少則回收,多則跳過
優點:old old generation上性能較 parallel 方式有提高
缺點:大部分server系統old generation內存佔用會達到60%-80%, 沒有那麼多理想的單元live object很少方便迅速回收,同時compact方面開銷比起parallel並沒明顯減少。

4. Concurent Mark-Sweep(CMS) Collector
young generation = parallel collector = 2
old = cms
同時不做 compact 操作。
優點:pause time會降低, pause敏感但CPU有空閑的場景需要建議使用策略4.
缺點:cpu佔用過多,cpu密集型伺服器不適合。另外碎片太多,每個object的存儲都要通過鏈表連續跳n個地方,空間浪費問題也會增大。

內存監控的方法:

1. jmap -heap pid
查看java 堆(heap)使用情況

using thread-local object allocation.
Parallel GC with 4 thread(s) //GC 方式

Heap Configuration: //堆內存初始化配置
MinHeapFreeRatio=40 //對應jvm啟動參數-XX:MinHeapFreeRatio設置JVM堆最小空閑比率(default 40)
MaxHeapFreeRatio=70 //對應jvm啟動參數 -XX:MaxHeapFreeRatio設置JVM堆最大空閑比率(default 70)
MaxHeapSize=512.0MB //對應jvm啟動參數-XX:MaxHeapSize=設置JVM堆的最大大小
NewSize = 1.0MB //對應jvm啟動參數-XX:NewSize=設置JVM堆的『新生代』的默認大小
MaxNewSize =4095MB //對應jvm啟動參數-XX:MaxNewSize=設置JVM堆的『新生代』的最大大小
OldSize = 4.0MB //對應jvm啟動參數-XX:OldSize=<value>:設置JVM堆的『老生代』的大小
NewRatio = 8 //對應jvm啟動參數-XX:NewRatio=:『新生代』和『老生代』的大小比率
SurvivorRatio = 8 //對應jvm啟動參數-XX:SurvivorRatio=設置年輕代中Eden區與Survivor區的大小比值
PermSize= 16.0MB //對應jvm啟動參數-XX:PermSize=<value>:設置JVM堆的『永生代』的初始大小
MaxPermSize=64.0MB //對應jvm啟動參數-XX:MaxPermSize=<value>:設置JVM堆的『永生代』的最大大小

Heap Usage: //堆內存分步
PS Young Generation
Eden Space: //Eden區內存分布
capacity = 20381696 (19.4375MB) //Eden區總容量
used = 20370032 (19.426376342773438MB) //Eden區已使用
free = 11664 (0.0111236572265625MB) //Eden區剩餘容量
99.94277218147106% used //Eden區使用比率
From Space: //其中一個Survivor區的內存分布
capacity = 8519680 (8.125MB)
used = 32768 (0.03125MB)
free = 8486912 (8.09375MB)
0.38461538461538464% used
To Space: //另一個Survivor區的內存分布
capacity = 9306112 (8.875MB)
used = 0 (0.0MB)
free = 9306112 (8.875MB)
0.0% used
PS Old Generation //當前的Old區內存分布
capacity = 366280704 (349.3125MB)
used = 322179848 (307.25464630126953MB)
free = 44100856 (42.05785369873047MB)
87.95982001825573% used
PS Perm Generation //當前的 「永生代」 內存分布
capacity = 32243712 (30.75MB)
used = 28918584 (27.57891082763672MB)
free = 3325128 (3.1710891723632812MB)
89.68751488662348% used

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

jps
-q只輸出進程ID,而不輸出類的短名稱
-m用於輸出傳遞給Java進程(主函數)的參數
-l完整路徑
-v顯示傳遞給jvm的參數

9、為什麼CMS GC時出現Concurrent Mode Failure

並發收集器(concurrentcollector)指的是回收年老代和持久代時,採用多個線程和應用線程並發執行,減少應用停頓時間,但如果參數設置不當,容易出現Concurrent ModeFailure現象,此時JVM將採用停頓的方式進行full gc,整個gc時間相當可觀,完全違背了採用CMS GC的初衷。
出現此現象的原因主要有兩個:一個是在年老代被用完之前不能完成對無引用對象的回收;一個是當新空間分配請求在年老代的剩餘空間中得到滿足。原文如(if theconcurrent collector is unable to finish reclaiming the unreachable objectsbefore the tenured generation fills up, or if an allocation cannot be satisfiedwith the available free space blocks in the tenured generation, then theapplication is paused and the collection is completed with all the applicationthreads stopped)。
出現此現象的具體gc日誌如下:
90003.167: [GC 90003.167: [ParNew: 261760K->0K(261952K), 0.0204310secs] 778897K->520196K(1310528K), 0.0207190 secs]
90006.049: [GC 90006.050: [ParNew: 261760K->0K(261952K), 0.0136380 secs]781956K->521446K(1310528K), 0.0138720 secs]
90010.628: [GC 90010.628: [ParNew: 261760K->261760K(261952K), 0.0000350secs]90010.628: [CMS (concurrent mode failure)[Unloadingclass sun.reflect.]
[Unloading class sun.reflect.]
[Unloading class sun.reflect.]
[Unloading class sun.reflect.]
[Unloading class sun.reflect.]
[Unloading class sun.reflect.]
[Unloading class sun.reflect.]
: 521446K->415777K(1048576K), 2.2550270 secs] 783206K->415777K(1310528K),2.2553820 secs]
90015.099: [GC 90015.099: [ParNew: 261760K->0K(261952K), 0.0198180 secs]677537K->418003K(1310528K), 0.0200650 secs]
90018.670: [GC 90018.670: [ParNew: 261760K->0K(261952K), 0.0131610 secs]679763K->419115K(1310528K), 0.0133750 secs]
90022.254: [GC 90022.254: [ParNew: 261760K->0K(261952K), 0.0151240 secs]680875K->420505K(1310528K), 0.0154180 secs]
當時的JVM參數如下:
-server -Xms1280m -Xmx1280m -Xmn256m -Xss256k -XX:PermSize=128m-XX:MaxPermSize=128m -XX:+UseConcMarkSweepGC -XX:+UseParNewGC-XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection -XX:+UseCMSInitiatingOccupancyOnly -XX:+CMSClassUnloadingEnabled-XX:+DisableExplicitGC -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
因為配置了+CMSClassUnloadingEnabled參數,所以出現Unloading classsun.reflect.的日誌,這是個好習慣,如果空間不夠時可以卸載類來釋放空間,以進行FULL GC,相反,如果gc日誌中出現了此日誌,應該檢查各代的大小設置是否合理。這里應用從啟動到上述現象出現時還沒有進行過CMS GC,出現concurrent modefailure現象的原因是年輕代GC(ParNew),年老代所剩下的空間不足以滿足年輕代,也就是開頭提到的原因二。要避免此現象,方法一是降低觸發CMS的閥值,即參數-XX:的值,默認值是68,所以這里調低到50,讓CMS GC盡早執行,以保證有足夠的空間,如下:
-server -Xms1280m -Xmx1280m -Xmn256m -Xss256k -XX:PermSize=128m-XX:MaxPermSize=128m -XX:+UseConcMarkSweepGC -XX:+UseParNewGC-XX:CMSFullGCsBeforeCompaction=1 -XX:+UseCMSCompactAtFullCollection -XX:+UseCMSInitiatingOccupancyOnly -XX:=50-XX:+CMSClassUnloadingEnabled -XX:+DisableExplicitGC -verbose:gc-XX:+PrintGCDetails -XX:+PrintGCTimeStamps
調完之後發現還是一樣的現象(這里有點不是很明白,年老代空間為1024m(1280m-256m),50%時觸發CMS GC,也就是在年老代512m的時候,剩下的堆空間有512m,就算年輕代全部裝進去應該也是夠的),所以懷疑是年輕代太大,有大的對象,在年老代有碎片的情況下將很難分配,所以有了第二個解決辦法,即減少年輕代大小,避免放入年老代時需要分配大的空間,同時調整full gc時壓縮碎片的頻次,減少持久代大小,以及將觸發CMS GC的閥值適當增大(因為年輕代小了,這個調大點沒關系,後面可以再調試這個參數),參數如下:
-server -Xms1280m -Xmx1280m -Xmn128m -Xss256k -XX:PermSize=96m -XX:MaxPermSize=96m -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:CMSFullGCsBeforeCompaction=1 -XX:+UseCMSCompactAtFullCollection -XX:+CMSParallelRemarkEnabled -XX:+UseCMSInitiatingOccupancyOnly -XX:=70 -XX:+CMSClassUnloadingEnabled -XX:+DisableExplicitGC -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
調整完後沒有那個現象了,這里主要起作用的就是調小年輕代大小。在年老代到達826m(觸發CMS閥值(1280-128)*0.7=806m)時出現了CMS GC,用時27ms,日誌如下:
705.444: [GC 705.445: [ParNew: 130944K->0K(131008K), 0.0197680 secs]954628K->826284K(1310656K), 0.0199720 secs]
705.467:[GC [1 CMS-initial-mark: 826284K(1179648K)] 826744K(1310656K), 0.0271540 secs]
705.494:[CMS-concurrent-mark-start]
706.717:[CMS-concurrent-mark: 1.223/1.223 secs]
706.717:[CMS-concurrent-preclean-start]
706.717:[CMS-concurrent-preclean: 0.000/0.000 secs]
706.742:[CMS-concurrent-abortable-preclean-start]
706.742:[CMS-concurrent-abortable-preclean: 0.000/0.000 secs]
707.067: [GC 707.067: [ParNew: 130944K->0K(131008K), 0.0160200 secs]957228K->827348K(1310656K), 0.0162110 secs]
707.796: [GC[YG occupancy: 66671 K (131008 K)]707.796: [Rescan (parallel) ,0.0278280 secs]707.824: [weak refs processing, 0.0420770 secs] [1 CMS-remark:827348K(1179648K)] 894019K(1310656K), 0.0711970 secs]
707.877: [CMS-concurrent-sweep-start]
708.453: [GC 708.454: [ParNew: 130944K->0K(131008K), 0.0203760 secs]848439K->718796K(1310656K), 0.0205780 secs]
709.833: [GC 709.833: [ParNew: 130944K->0K(131008K), 0.0160170 secs]430484K->301411K(1310656K), 0.0161840 secs]
709.916: [CMS-concurrent-sweep: 1.974/2.040 secs]
709.916: [CMS-concurrent-reset-start]
709.951: [CMS-concurrent-reset: 0.034/0.034 secs]
711.187: [GC 711.187: [ParNew: 130944K->0K(131008K), 0.0130890 secs]413136K->283326K(1310656K), 0.0132600 secs]
觀察一段時間的gc情況,gc效率也很高,單次YGCT<20ms,FGCT <40ms:
$ jstat -gcutil 31935 1000
S0 S1 E O P YGC YGCT FGC FGCT GCT
0.00 0.00 64.29 36.47 73.15 1293 19.514 6 0.211 19.725
0.00 0.00 64.33 36.47 73.15 1293 19.514 6 0.211 19.725
0.00 0.00 64.41 36.47 73.15 1293 19.514 6 0.211 19.725
0.00 0.00 64.45 36.47 73.15 1293 19.514 6 0.211 19.725
0.00 0.00 64.49 36.47 73.15 1293 19.514 6 0.211 19.725
0.00 0.00 64.58 36.47 73.15 1293 19.514 6 0.211 19.725
0.00 0.00 64.63 36.47 73.15 1293 19.514 6 0.211 19.725
0.00 0.00 64.69 36.47 73.15 1293 19.514 6 0.211 19.725
0.00 0.00 64.72 36.47 73.15 1293 19.514 6 0.211 19.725
0.00 0.00 64.75 36.47 73.15 1293 19.514 6 0.211 19.725
0.00 0.00 64.79 36.47 73.15 1293 19.514 6 0.211 19.725
0.00 0.00 64.84 36.47 73.15 1293 19.514 6 0.211 19.725
0.00 0.00 64.90 36.47 73.15 1293 19.514 6 0.211 19.725
0.00 0.00 64.95 36.47 73.15 1293 19.514 6 0.211 19.725
0.00 0.00 64.99 36.47 73.15 1293 19.514 6 0.211 19.725
這時,想再測試下-XX:的值,調到80時又出現了上述現象Concurrent ModeFailure,啟動後還沒進行過CMS GC,在年老代914m時就出現了:
759.994: [GC 759.994: [ParNew: 130944K->0K(131008K), 0.0172910 secs]1040896K->911480K(1310656K), 0.0174730 secs]
760.879: [GC 760.879: [ParNew: 130944K->0K(131008K), 0.0300920 secs]1042424K->914190K(1310656K), 0.0302950 secs]
761.768: [GC 761.769: [ParNew: 130944K->130944K(131008K), 0.0000340secs]761.769: [CMS (concurrent mode failure)[Unloading classsun.reflect.GeneratedMethodAccessor342]edMethodAccessor348]
[Unloading class sun.reflect.GeneratedMethodAccessor411]
[Unloading class sun.reflect.GeneratedMethodAccessor407]
[Unloading class sun.reflect.GeneratedMethodAccessor541]
最後總結下,出現Concurrent ModeFailure現象時,解決辦法就是要讓年老代留有足夠的空間,以保證新對象空間的分配。另外在JVM BUG中有提到,JDK1.5_09版本之前,JVM參數-XX:是無效的,我這里應用環境的版本是JDK1.5_08,從gc日誌來看是可以生效的。
GC時還有一個常見的錯誤PromotionFailed,解決辦法類似,也是調整年輕代和年老代的比例,還有CMSGC的時機。

與cmsgclog相關的知識