2010-12-16T23:04:39.452-0600: [Full GC (System)
[CMS: 418061K->428608K(16384K), 0.2539726 secs]
418749K->4288608K(31168K),
[CMS Perm : 32428K->32428K(65536K)],0.2540393 secs]
[Times: user=0.12 sys=0.01, real=0.25 secs]
併發的Permanent代垃圾回收
FullGC發生可能是由於permanent空間滿了引起的,監控FullGC垃圾回收信息,然後觀察Permanent代的佔用量,判斷FullGC是否是由於permanent區域滿了引起的。下面是一個由於permanent代滿了引起的FullGC的例子:
2010-12-16T17:14:32.533-0600: [Full GC
[CMS: 95401K->287072K(1048576K), 0.5317934 secs]
482111K->287072K(5190464K),
[CMS Perm : 65534K->58281K(65536K)], 0.5319635 secs]
[Times: user=0.53 sys=0.00, real=0.53 secs]
注意permanent代的空間佔用量,通過CMS Perm :標籤識別。permanent代空間大小是括號裏面的值,65536K。在FullGC之前permanent代的佔用量是->左邊的值,65534K,FullGC之後的值是58281K。可以看到的是,在FullGC之前,permanent代的佔用量以及基本上和permanent代的容量非常接近了,這個說明,FullGC是由Permanent代空間溢出導致的。同樣需要注意的是,old代還沒有到溢出空間的時候,而且沒有證據說明CMS週期啓動了。
HotSpot VM默認情況下,CMS不會垃圾回收permanent代空間,儘管垃圾回收日誌裏面有CMS Perm標籤。爲讓CMS回收permanent代的空間,可以用過下面這個命令選項來做到:
-XX:+CMSClassUnloadingEnabled
如果使用Java 6 update 3及之前的版本,你必須指定一個命令選項:
-XX:+CMSPermGenSweepingEnabled
你可以控制permanent的空間佔用率來啓動CMS permanent代垃圾回收通過下面這個命令選項:
-XX:CMSInitiatingPermOccupancyFraction=<percent>
這個參數的功能和-XX:CMSInitiatingOccupancyFraction很像,他指的是啓動CMS週期的permanent代的佔用率。這個參數同樣需要和-XX:+CMSClassUnloadingEnabled配合使用。如果你想一直使用-XX:CMSInitiatingPermOccupancyFraction的值作爲啓動CMS週期的條件,你必須要指定另外一個選項:
-XX:+UseCMSInitiatingOccupancyOnly
CMS暫停時間優化
在CMS週期裏面,有兩個階段是stop-the-world階段,這個階段所有的應用線程都被阻塞了。這兩階段是“初始標記”階段和“再標記”階段,儘管初始標記解決是單線程的,但是通過不需要花費太長時間,至少比其他垃圾回收的時間短。再標記階段是多線程的,線程數可通過命令選項來控制:
-XX:ParallelGCThreads=<n>
在Java 6 update 23之後,默認值是通過Runtime.availableProcessors()來確定的,不過是建立在返回值小於等於8的情況下,反之,會使用Runtime.availableProcessors()*5/8作爲線程數。如果有多個程序運行在同一個機器上面,建議使用比默認線程數更少的線程數。否則,垃圾回收可能會引起其他應用的性能下降,由於在同一個時刻,垃圾回收器使用太多的線程。
在某些情況下設置下面這個選項可以減少再標記的時間:
-XX:+CMSScavengeBeforeRemark
這個選項強制HotSpot VM在FullGC之前執行MinorGC,在再標記步驟之前做MinorGC,可以減少再標記的工作量,由於減少了young代的對象數,這些對象能夠在old代獲取到的。
如果應用有大量的引用或者finalizable對象需要處理,指定下面這個選項可以減少垃圾回收的時間:
-XX:+ParallelRefProcEnabled
這個選項可以用HotSpot VM的任何一種垃圾回收器上,他會是用多個的引用處理線程,而不是單個線程。這個選項不會啓用多線程運行方法的finalizer。他會使用很多線程去發現需要排隊通知的finalizable對象。
下一步
這一步結束,你需要看看應用的延遲需要是否滿足了,無論是使用throughput垃圾回收器或者併發垃圾回收器。如果沒有能夠滿足應用的需要,那麼回頭看看需求是否合理或者修改應用程序。如果滿足了應用的需求,那麼我們就進入下一步——優化吞吐量。