理解hotspot的CMS GC

理解hotspot CMS(併發標記清除垃圾收集器)中的暫停
CMS(concurrent mark sweep)併發清除標記的縮寫,CMS儘可能的減少垃圾回收中的暫停,但是它仍然需要很少的暫停。暫停是指,停止應用程序的運行,JVM啓動線程進行垃圾回收。CMS主要是針對old space空間的垃圾回收,copy collector主要針對young space的垃圾回收工作。啓用CMS需要在JVM設置–XX:+UseConcMarkSweepGC。

CMS工作的基本階段分爲:
初始化標記:第一次暫停,初始化標記,在old space收集根集合,標記存活對象
併發標記:運行時標記,遍歷old space,標記存活對象
準備清理:併發的標記前一階段被修改的對象
重新標記:第二次暫停,檢查,標記,檢查髒頁的對象,標記前一階段被修改的對象
併發清理:運行過程中清理,掃面old space,釋放不可到達對象佔用的空間
併發重置:此次CMS結束後,重設CMS狀態等待下次CMS的觸發

碎片問題
因爲年老代的併發收集器使用標記,清除算法,所以不會對堆進行壓縮。當收集器回收時,他會把相鄰的空間進行合併,這樣可以分配給較大的對象。但是,當堆空間較小時,運行一段時間以後,就會出現"碎片",如果併發收集器找不到足夠的空間分配給新對象,那麼併發收集器將會停止,然後使用傳統的標記,清除方式進行回收。如果出現"碎片",可能需要進行如下配置:
-XX:+UseCMSCompactAtFullCollection:使用併發收集器時,開啓對年老代的壓縮。 -XX:CMSFullGCsBeforeCompaction=0:上面配置開啓的情況下這裏設置多少次Full GC後,對年老代進行壓縮。

CMS中的暫停時長
第一階段暫停:初始化標記
CMS標記可到達對象,收集根集,根集的引用主要來自線程棧和young space,收集線程棧的引用非常快,少於一毫秒。收集young space的引用依賴於young space空間對象的數量,young space包括Eden space和Survivor space,如果在young space垃圾回收發生之後,這是young space已經被清理了,Eden space爲空,Survivor space很小,這是後收集只需要很少的時間。如果在young space滿的時候收集,需要花很長的時間。所以如果選擇CMS,可以通過設置CMS初始化標記的延遲時間,延遲到young space垃圾回收之後開始工作標記。–XX:CMSWaitDuration=這個值可以長於young space垃圾回收的週期。

第二階段暫停:重新標記
前期的併發標記幾乎完成了大部分的標記工作,但是在併發標記的過程中,有的對象被修改了,爲了達到更精確的目的,需要暫停應用程序,檢查從併發標記階段開始被修改的對象,重新標記。卡表(card table)技術就是用來記錄在標記過程中old space被修改的頁。Remark階段時間主要花在young space的掃描上,所以保證remark之前進行一次young space的垃圾回收。–XX:+CMSScavengeBeforeRemark強制JVM執行這個過程。

CMS什麼時候開始?
CMS應該在old space撐爆之前啓動,當old space的空閒空間越來越少,低於某個閾值的時候將激發CMS。實際情況是,CMS可能被延遲到young space垃圾回收完成以後。
正常情況是,young space的對象在垃圾回收後倖存下來後,會被晉升到old space。所以CMS循環總是發生在young space回收之後,這樣初始化標記就會很快。但是也有特殊情況,大對象總是直接分配到old space,導致空閒空間下降後,引發CMS時,還未做young space回收,Eden space裏有很多對象,初始化標記要花很長時間。避免這種長時間的暫停可以配置–XX:CMSWaitDuration延遲CMS的執行。
我們還可以配置old space引發CMS的閾值,這個閾值表示的old space的使用率, XX:+UseCMSInitiatingOccupancyOnly XX:CMSInitiatingOccupancyFraction=70,當old space的空間佔用達到70%後會強制JVM執行CMS。
如果配置了 XX:+ExplicitGCInvokesConcurrent,當應用程序調用System.gc(),JVM會啓動CMS。
如果配置了-XX:+DisableExplicitGC 標誌自動將 System.gc() 調用轉換成一個空操作。

CMS中的Full GC
以下情況會導致Full GC:
CMS回收垃圾速度不夠快;
垃圾回收週期啓動太晚了;
Old space碎片過多;
JVM本省設置內存不夠;
上面三種情況都會導致old space沒有足夠的空間分配給新對象,引發Full GC,應用程序會拋出內存溢出異常。內存溢出,是指系統沒有內存分配給新對象,內存泄露是指,爲對象分配的內存,使用後沒有釋放,這塊內存一直被佔用,導致總的可用內存空間的減少。

永久代空間的回收
另外一個導致full GC的原因在於永久代(permanent space),因爲默認情況下,JVM的CMS是不會去回收永久代的對象。如果應用程序使用了大量的類加載和發射,就需要回收永久代的對象。 XX:+CMSClassUnloadingEnabled允許CMS回收永久代的不可到達對象。

利用多核
CMS分階段工作,很多階段是併發執行的,其他階段也可以並行執行,通過下面配置可以減小應用程序被暫停的時長。
XX:+CMSConcurrentMTEnabled 允許CMS在併發階段使用多核.
XX:+ConcGCThreads= 指定CMS併發階段的線程數.
XX:+ParallelGCThreads= 指定在暫停階段並行工作的線程數(默認情況等於物理核的數量).
XX:+UseParNewGC 指示JVM使用並行收集器收集young space結合CMS工作.


以下是對JVM中常見配置總結
-Xms128m表示JVM Heap(堆內存)最小尺寸128MB,初始分配
-Xmx512m表示JVM Heap(堆內存)最大允許的尺寸256MB,按需分配。此值可以設置與-Xmx相同,以避免每次垃圾回收完成後JVM重新分配內存。

-Xss1m 設置每個線程的堆棧大小1M。JDK5.0以後每個線程堆棧大小爲1M,以前每個線程堆棧大小爲256K。根據應用的線程所需內存大小進行調整。在相同物理內存下,減小這個值能生成更多的線程。但是操作系統對一個進程內的線程數還是有限制的,不能無限生成,經驗值在3000~5000左右。

-Xmn2g:設置年輕代大小爲2G。整個堆大小=年輕代大小+年老代大小+持久代大小。持久代一般固定大小爲64m,所以增大年輕代後,將會減小年老代大小。此值對系統性能影響較大,Sun官方推薦配置爲整個堆的3/8。
使用-XX:NewSize和-XX:MaxNewsize設置新域的初始值和最大值。
-XX:NewRatio=4:設置年輕代(包括Eden和兩個Survivor區)與年老代的比值(除去持久代)。設置爲4,則年輕代與年老代所佔比值爲1:4,年輕代佔整個堆棧的1/5
-XX:SurvivorRatio=4:設置年輕代中Eden區與Survivor區的大小比值。設置爲4,則兩個Survivor區與一個Eden區的比值爲2:4,一個Survivor區佔整個年輕代的1/6 。

PermSize和maxPermSize表示虛擬機爲java永久生成對象(Permanate generation)如class對象、方法對象這些可反射對象分配內存限制,這些內存不包括在Heap(堆內存)區之中。
-XX:PermSize=64MB 最小尺寸,初始分配
-XX:MaxPermSize=256MB 最大允許分配尺寸,按需分配
MaxPermSize缺省值和-server -client選項相關。
-server選項下默認MaxPermSize爲64m
-client選項下默認MaxPermSize爲32m

-XX:MaxTenuringThreshold=0:設置垃圾最大年齡。如果設置爲0的話,則年輕代對象不經過Survivor區,直接進入年老代。 對於年老代比較多的應用,可以提高效率。如果將此值設置爲一個較大值,則年輕代對象會在Survivor區進行多次複製,這樣可以增加對象再年輕代的存活 時間,增加在年輕代即被回收的概率

收集器設置
-XX:+UseSerialGC:設置串行收集器
-XX:+UseParallelGC:設置並行收集器
-XX:+UseParalledlOldGC:設置並行年老代收集器
-XX:+UseConcMarkSweepGC:設置併發收集器
-XX:+UseAdaptiveSizePolicy:設置此選項後,並行收集器會自動選擇年輕代區大小和相應的Survivor區比例,以達到目標系統規定的最低相應時間或者收集頻率等,此值建議使用並行收集器時,一直打開。
-XX:+UseConcMarkSweepGC:設置年老代爲併發收集。測試中配置這個以後,-XX:NewRatio=4的配置失效了,原因不明。所以,此時年輕代大小最好用-Xmn設置。
-XX:+UseParNewGC:設置年輕代爲並行收集。可與CMS收集同時使用。JDK5.0以上,JVM會根據系統配置自行設置,所以無需再設置此值。
-XX:CMSFullGCsBeforeCompaction:由於併發收集器不對內存空間進行壓縮、整理,所以運行一段時間以後會產生“碎片”,使得運行效率降低。此值設置運行多少次GC以後對內存空間進行壓縮、整理。
-XX:+UseCMSCompactAtFullCollection:打開對年老代的壓縮。可能會影響性能,但是可以消除碎片。

並行收集器設置
-XX:ParallelGCThreads=n:設置並行收集器收集時使用的CPU數。並行收集線程數。
-XX:MaxGCPauseMillis=n:設置並行收集最大暫停時間
-XX:GCTimeRatio=n:設置垃圾回收時間佔程序運行時間的百分比。公式爲1/(1+n)
併發收集器設置
-XX:+CMSIncrementalMode:設置爲增量模式。適用於單CPU情況。
-XX:ParallelGCThreads=n:設置併發收集器年輕代收集方式爲並行收集時,使用的CPU數。並行收集線程數。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章