雖然大多數應用程序使用JVM的默認設置就能很好地工做,仍然有很多應用程序須要對JVM進行額外的配置才能達到其指望的性能要求。html
如今JVM爲了知足各類應用的須要,爲程序運行提供了大量的JVM配置選項。不幸的是,針對一個應用程序進行的JVM調優(配置)可能並不適用於另外一個應用程序。安全
注意:爲了達到應用程序的系統需求,可能須要進行屢次迭代才能得到應用程序的性能要求。性能優化
吞吐量、響應時間、內存消耗量、可用性、可管理性等。服務器
可用性是對應用程序處於可操做,可以使用狀態的度量。多線程
可用性需求指的是當引用程序的某些組件發生故障或失效時,應用程序或應用程序的一部分在多大程度上還能夠繼續提供服務。併發
可管理性是對由運行,監控應用程序而產生的操做性開銷的度量,同時也包含了配置應用程序的難易程度。佈局
吞吐量是對單位時間內處理工做量的度量。性能
涉及吞吐量需求時,通常不考慮它對延遲或響應時間的影響。一般狀況下,增長吞吐量的代價是延遲的增長或內存使用的增長。測試
延遲或響應性是對應用程序收到指令開始工做直到完成該工做消耗時間的度量。優化
定義延遲或響應性需求時並不考慮程序的吞吐量,一般狀況下,提升響應性或縮小延遲的代價是更低的吞吐量或更多的內存消耗。
內存佔用指的同等程度的吞吐量,延遲,可用性和可管理性前提下,運行應用程序所需的內存大小。
內存佔用一般以運行應用程序須要的Java堆大小或運行應用程序須要的總內存大小來表述。通常狀況下,經過增大Java堆的方式增長可用內存可以提升吞吐量,下降延遲或兼顧二者。
啓動時間是應用程序初始化所消耗的時間。
JVM部署模式選擇指的是將應用程序部署到單個JVM實例上,仍是部署到多個JVM實例上。
採用單JVM部署模式的應用程序存在單點故障的問題。
將JVM應用程序部署到多個JVM實例可以得到更好的可用性,以及更低延遲的可能性。
多JVM部署模式可能提供更低的延遲,這是由於多JVM部署模式下,Java堆一般比較小,較小的堆在垃圾收集時產生的停頓更小。一般狀況下,垃圾收集所產生的停頓是影響應用程序延遲性的最主要因素。
通常狀況下,使用的JVM數目越少越好,監控和管理成本就越低,消耗的總內存也更少。
HotSpot VM有3種運行模式:Client模式、Server模式、Tiered Server模式。
Client模式的特色是啓動快、佔用內存少、JIT編譯器生成代碼的速度也更快。
Server模式則提供了更復雜的生成碼優化功能,這個功能對於服務器應用而言尤爲重要。大多數Server模式的JIT編譯優化都要消耗額外的時間以收集更多的應用程序行爲信息、爲應用程序運行生成更優的生成碼。
Tiered Server模式結合了Client和Server運行模式的長處,即快速啓動和高效的生成碼。經過-server -XX:+TieredCompilation命令行選項能夠啓動Tiered Server模式。
除了Client模式和Server模式,JVM部署時還有另外一個選項:32位JVM或64JVM。
注意:64位HotSpot虛擬機中沒有提供Client模式。
對於使用-XX:+UseCompressedOops選項的64位HotSpot VM,最大堆小於等於26GB時性能最好。
Java 6 Update 18以後的HotSpot VM可以根據最大Java堆的狀況自動啓動-XX:+UseCompressedOops。
HotSpot VM提供了多個垃圾收集器:Serial收集器,Throughput(PS)收集器,Mostly-Concurrent(CMS)收集器及G1收集器等。具體參考【垃圾收集器】
介紹影響垃圾收集性能的三個主要的屬性,垃圾收集調優的三個基本原則,HotSpot VM垃圾收集調優時須要採集的信息。
吞吐量:不考慮垃圾收集引發的停頓時間或內存消耗,垃圾收集器能支撐應用程序達到的最高性能指標;
延遲:度量標準是縮短因爲垃圾收集引發的停頓時間或徹底消除因垃圾收集所引發的停頓,避免應用程序運行時發生抖動;
內存佔用:垃圾收集器流暢運行所須要的內存數量;
這其中任何一個屬性性能的提升幾乎都是以另外一個或兩個屬性性能的損失做代價的。換句話說,某一個屬性上的性能提升總會犧牲另外一個或兩個屬性。然而,對大多數的應用而言,極少出現這三個屬性的重要程度都同等的狀況。不少時候,某一個或兩個屬性的性能要比另外一個重要。
調優JVM垃圾收集的過程當中謹記這三條原則能幫助你更輕鬆地調優垃圾收集,達到應用程序的性能要求。
在後面的調優過程當中,要根據監控垃圾收集時得到的指標決定JVM的調優方案。GC日誌是收集調優所須要信息的最好途徑,能夠經過命令行開啓HotSpot VM的GC統計信息採集功能。
爲了定位問題,即便在生產系統上開啓GC日誌也是個不錯的方案,開啓GC日誌對性能的影響極小。
-XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:<filename>
-XX:+PrintGCTimeStamps打印從VM啓動直到GC開始所經歷的時間(單位:sec),若是須要用日曆時間格式打印時間戳,可使用-XX:+PrintGCDateStamps;
-XX:+PrintGCDetails提供垃圾收集器相關的統計數據,該選項的輸出與使用的垃圾收集器密切相關,因此使用不一樣的垃圾收集器輸出結果會有不一樣;
-Xloggc:<filename>選項能夠指定將GC的日誌信息記錄到名爲filename的文件中。
關於GC日誌解釋,請參考
針對高延遲問題調優HotSpot VM時,有兩個參數能夠設置:
-XX:+PrintGCApplicationStoppedTime得到應用程序因爲執行VM安全點操做而阻塞的時間
-XX:+PrintGCApplicationConcurrentTime兩個安全點操做之間應用程序運行的時間
日誌樣例以下:
應用運行了0.0776117 seconds
暫停了0.0001124 seconds
到達安全點的耗時0.0000138 seconds
注:安全點操做使JVM進入一種狀態:全部的Java應用線程都被阻塞,安全點操做經常使用於虛擬機須要進行內部操做時,此時全部的Java線程都被顯式地置於阻塞狀態且不能修改Java堆的狀況。
-XX:+PrintSafepointStatistics能夠將垃圾收集的安全點與其餘的安全點區分開來。
這一步的調優將爲咱們定義運行應用程序須要的Java堆的大小提供有力的依據,能夠知道應用程序有多少活躍數據。
活躍數據的大小是指,應用程序穩定運行時長期存活對象所佔用的Java堆內存量。換句話說,它是應用程序運行於穩定態時,Full GC以後Java堆所佔用的空間大小。
具體Java堆的佈局能夠參考【Java內存劃分】 ,能夠幫助咱們肯定應用程序使用Java堆的大小、微調影響垃圾收集器性能的空間大小。
-Xms設置堆的初始和最小值,這個空間包括新生代和老年代總和。
-Xmx設置堆的最大值,這個空間包括新生代和老年代總和。
-XX:NewSize設置新生代的初始和最小值,若是設置了該參數,也應該同時設置-XX:MaxNewSize。
-XX:MaxNewSize設置新生代的最大值,若是設置了該參數,也應該同時設置-XX:NewSize。
-Xmn同時設置新生代的初始,最小和最大值,若是指望-XX:NewSize和-XX:MaxNewSize相等,則這是一個便利的參數。
備註:關注吞吐量和延遲的Java應用程序應該將-Xms和-Xmx設定爲同一值。這是由於不管擴展仍是縮減新生代空間或老年代空間都須要進行Full GC,而Full GC會下降程序的吞吐量並致使更長的延遲。
老年代空間的大小會根據新生代的大小隱式設定。
老年代的初始值爲(-Xmx)- (-XX:NewSize)
老年代的最小值爲(-Xmx)- (-XX:MaxNewSize)
-XX:MetaspaceSize設置元空間的初始和最小值。
-XX:MaxMetaspaceSize設置元空間的最大值。
若是不顯式指定堆大小,如初始值,最大值,新生代大小,Metaspace大小,HotSpot能夠經過名爲「自動調優」的自適應調優功能,依據系統配置自動選擇合適的值。
新生代、老年代或Metaspace這三個空間中的任何一個不能知足內存分配請求時,就會發生垃圾收集。
新生代是觸發Minor GC,老年代和Metaspace觸發Full GC。
Full GC會對新生代,老年代和Metaspace進行回收。
其中開啓-XX:+UseParallelGC或-XX:+UseParallelOldGC時,若是關閉-XX:-ScavengeBeforeFullGC,HotSpot VM在Full GC以前不會進行Minor GC,但Full GC過程當中依然會收集新生代;若是開啓-XX:+ScavengeBeforeFullGC,HotSpot VM在Full GC前會先作一次Minor GC,分擔一部分Full GC本來要作的工做。
經過命令行-XX:+PrintCommandLineFlags能夠查看堆的初始值(-XX:InitialHeapSize)和最大值(-XX:MaxHeapSize)。
若是GC日誌中出現OutOfMemoryError,能夠嘗試經過增長JVM可用物理內存緩解。尤爲要關注引發OutOfMemoryError的堆空間,確保增長其大小。例如,對老年代引發的OutOfMemoryErrors,增長-Xms和-Xmx;對Metaspace空間引發的OutOfMemoryErrors,增長-XX:Metaspace和-XX:MaxMetaspace值。
活躍數據大小是應用程序運行與穩定態時,長期存活的對象在Java堆中佔用的空間大小。也就是說,活躍數據大小是應用程序運行於穩定態,Full GC以後Java堆中老年代和Metaspace佔用的空間大小。
Java應用的活躍數據大小能夠經過GC日誌收集,活躍數據大小包括下面的內容:
爲了更好度量應用程序的活躍數據大小,最好在屢次Full GC以後再查看Java堆的佔用狀況,另外,須要確保Full GC發生時,應用程序正處於穩定態。
如何根據統計的活躍數據大小,肯定Java堆的初始大小。
通用法則之一:將Java堆的初始值-Xms和最大值-Xmx設置爲老年代活躍數據大小的3~4倍。
通用法則之二:Metaspace的初始值-XX:MetaspaceSize及最大值-XX:MaxMetaspaceSize應該比Metaspace活躍數據大1.2~1.5倍。
通用法則之三:新生代空間應該爲老年代空間活躍數據的1~1.5倍。
通用法則之四:若是Java堆的初始值及最大值爲活躍數據大小的3~4倍,新生代爲活躍數據的1~1.5倍,老年代應設置爲活躍數據大小的2~3倍。
總結:計算Java堆大小能夠參考下表:
空間 | 參數 | 佔用倍數 | 備註 |
Java堆 | -Xms和-Xmx | 3~4Full GC後的老年代空間佔用量 | |
Metaspace | -XX:MetaspaceSize和-XX:MaxMetaspaceSize | 1.2~1.5倍Full GC後的Metaspace佔用量 | |
新生代 | -Xmn或-XX:NewSize和-XX:MaxNewSize | 1~1.5倍Full GC後的老年代佔用量 | |
老年代 | Java堆減去新生代大小 | 2~3倍Full GC後的老年代佔用量 |
本節介紹的計算出來的Java堆大小並不表明Java應用程序的總內存佔用。
另外,Java堆不必定是最耗應用程序內存的。例如,應用程序的線程棧可能須要較多的內存,線程的數目越多,消耗在線程棧上的內存就越多。應用程序中,方法調用的層次越深,線程棧佔用的空間也越大。
謹記一點,調優過程當中,這一步操做可能致使應用程序沒法達到內存需求。出現這種狀況,要麼須要回顧或修改應用程序的內存佔用需求,要麼須要調整應用程序,進行Java堆的分析,修改應用程序,減小對象分配或對象保持,這些都是能夠採用的處理方法。減小對象分配,或更重要的,減小對象保持能夠減小活躍數據的大小。
這一步中計算出的Java堆大小僅僅是一個出發點。
這一步調優的目的是達到程序的延遲性需求,包括多個活動的迭代:優化Java堆大小的配置,評估GC的持續時間和頻率、是否可能切換到不一樣的垃圾收集器以及發生垃圾收集器切換以後進一步的內存調優。
這一步調優有兩個可能的結果:
評估垃圾收集器對延遲性影響的過程當中將進行以下的活動:
測量Minor GC的持續時間和頻率堆優化Java堆的大小相當重要,Minor GC的持續時間和頻率決定了優化後新生代的大小;
最差狀況下的Full GC持續時間和頻率決定了老年代的大小和垃圾收集器的切換:是否須要從PS收集器轉向CMS收集器。
應用程序的系統性需求:
根據垃圾收集的統計數據Minor GC的持續時間和頻率能夠肯定新生代空間的大小。
Minor GC須要的時間與新生代中可訪問的對象數直接相關,一般狀況下,新生代空間越小,Minor GC持續的時間越短,減小新生代空間又會增大Minor GC的頻率。以一樣的對象分配速度,較小的新生代空間在很短的時間內就會被填滿,增大新生代空間能夠減小Minor GC的頻率。
分析GC數據時,若是發現Minor GC的時間過長,修正的方法是減小新生代空間,若是Minor GC頻率過高,修正的方法是增長新生代空間。
真正達到應用程序的平均延遲要求以前可能要經歷屢次迭代,調整新生代空間大小時,儘可能保持老年代空間大小恆定。
如何估算增長多大的新生代來知足GC頻率的要求?
實例:假設應用程序的Minor GC頻率要求是每5秒一次,目前Minor GC的評論是每2.147秒一次,因爲應用程序的Minor GC頻率要求低於計算出的頻率,咱們能夠增長新生代的空間大小。根據新生代空間的當前大小和平均Minor GC的頻率,可以大體估算出能夠增長多少新生代空間。
(1)這個例子中,填充滿2048MB新生代空間平均耗時2.147;
(2)假設對象分配速度是恆定的,那麼須要增長2.3 = 5/2.147秒;
(3)也就是說若是2.147秒能夠填滿2048MB空間,那麼5秒能夠填滿大約4700MB空間;
(4)所以,爲了達到5秒的Minor GC頻率目標,新生代空間大小須要調整爲4700MB。
調整新生代空間時,要注意下面的幾個準則:
若是隻考慮Minor GC引發的延遲,而調整新生代的大小又沒法知足應用程序的平均停頓時間或延遲性要求,就只能修改應用程序或改變JVM的部署模式,在多個JVM上部署應用程序,或吸怪應用程序的平均延遲性要求。
評估Full GC引入的最差停頓時間和Full GC的頻率。
發生於穩定態的Full GC的持續時間是應用程序的最差Full GC停頓時間,對Full GC頻率的預估應該依據對象提高率進行計算。提高率能夠依據老年代空間佔用的增加量和每次Minor GC後新生代的空間佔用計算得出,老年代空間佔用狀況能夠經過Minor GC以後Java堆的佔用狀況減去同一次Minor GC後新生代的空間佔用獲得。
實例:有以下的Minor GC日誌
從日誌能夠看出,Java堆的帶席位6144MB,新生代大小爲2048MB,老年代爲6144 - 2048 = 4096MB;
假設活躍數據大小爲1370MB,則老年代中有空閒空間4096 - 1370 = 2726MB,須要多長時間才能填滿2726MB?
每次Minor GC以後老年代的佔用狀況:
每次Minor GC以後老年代的增加量:
除了提高率,還須要知道Minor GC的頻率,假設平均Minor GC的頻率是每隔2.147秒一次。所以提高率爲21494KB / 2.147,大約爲10MB/sec。所以填滿2726MB可用老年代的時間大約爲272.6秒。
所以,根據前面的GC分析,應用程序能夠預期的最差Full GC頻率是每272.6秒一次。
若是預期或觀測到Full GC的頻率已經遠遠不能達到應用程序最差Full GC頻率要求,就應該增大老年代空間的大小,這個方法能夠幫助下降Full GC的頻率,增長老年代空間的大小時注意保持新生代空間大小恆定。
若是老年代太小,有可能會出現頻繁Full GC,老年代中幾乎沒有任何空間被回收,與此同時,新生代中總有大量的對象佔用空間,當老年代中空間沒法接納重新生代中提高的對象時,這些對象會被「退還」到新生代空間中。
應用程序的吞吐量一般在應用層面而不是在JVM的層面進行度量。
見9.6小結
對PS收集器進行吞吐量性能調優的目標是儘量避免發生Full GC或更理想的狀況下在穩定態時永遠不發生Full GC。爲了達到這個目標須要優化對象老化頻率,經過顯式地微調Survivor空間能夠實現對象老化的優化。你能夠將Eden空間變得更大,從而下降Minor GC的頻率,確保老年代有足夠的空間持有應用程序的活躍數據。若是對象沒有理想的老化頻率,一些非長期存活對象被提高到了老年代時,能夠增長一些額外的老年代空間來應對這種狀況。
PS收集器提供的吞吐量性能是HotSpot VM諸多垃圾收集器種最好的。PS收集器默認啓用了一個稱爲自適應大小調整的特性。自適應大小調整根據對象分配以及存活率自動地對新生代Eden和Survivor空間進行調整以最優化對象老化頻率。使用以下的選項能夠禁用自使用大小調整:-XX:-UseAdaptiveSizePolicy。只有PS收集器支持自適應大小調整。嘗試在非PS收集器上啓用或禁用自適應大小調整都不會有任何效果,即這種操做時空操做。
-XX:+PrintAdaptiveSizePolicy(不管UseAdaptiveSizePolicy是否開啓)能夠生成更詳細的Survivor空間佔用日誌。實例以下:
survived:標示To Survivor空間中存活對象的大小,即minor GC以後,To Survivor空間的空間佔用的空間大小;
promote: 新生代提高至老年代空間的對象大小;
overflow:是否有Survivor空間的對象溢出到老年代空間,也就是說To Survivor空間是否有足夠的空閒空間容納垃圾收集時Eden和From Survivor空間中的倖存對象。
爲了達到最優吞吐量性能,理想狀況下,應用程序運行於穩定態時,Survivor空間不該該發生溢出。
若是Survivor空間在穩定態發生溢出,對象將在其達到極限年齡老化死去以前被提高到老年代空間。頻繁的Survivor空間溢出會致使頻繁的Full GC。
使用CMS收集器,老年代垃圾收集線程和應用程序線程能實現最大的並行度,這爲同時下降最差延遲出現的頻率以及最差延遲的持續時間,避免發生長時間的GC提供了機會。
CMS並不進行壓縮,因此這一效果主要是經過避免老年代空間發生STW壓縮式垃圾來收集實現的。一旦老年代溢出就會觸發STW壓縮式垃圾收集。
STW這一的壓縮式GC與Full GC之間存在微妙的區別,在CMS中,若是老年代沒有足夠的空間處理來自新生代空間的對象晉升,只會在老年代空間觸發一次STW的壓縮式GC。發生Full GC時,除非使用-XX:-ScavengeBeforeFullGC選項,不然老年代和新生代的空間都會進行垃圾收集。
調優CMS收集器的目的是避免發生STW的壓縮式GC。
使用CMS,若是老年代空間用盡,就會觸發一個單線程STW壓縮式的垃圾收集。
相對於Parallel Old收集器的Full GC而言,CMS垃圾收集一般的持續時間更長。
幾個方面因素使得CMS收集器的調優很是具備挑戰性:
(1)對象重新生代提高至老年代的蘇聯;
(2)並行老年代垃圾收集線程回收空間的速率;
(3)因爲CMS收集器回收位於對象之間的垃圾對象而形成老年代空間的碎片化
解決碎片化的方法有:(1)壓縮老年代空間(2)加大老年代空間(3)減小對象重新生代提高到老年代的比率
Survivor空間時新生代空間的一部分。具體參考http://www.cnblogs.com/lujiango/p/7690885.html
備註:跟CMS不一樣,Parallel Old收集器默認開啓一個「自適應大小調整」的功能,可以自動調整Eden和Survivor大小。可是通用的操做時相同的,好比對象如何分配,如何從Eden複製到Survivor空間,若是在Survivor之間複製跟CMS收集器是一致的。
在全部的HotSpot垃圾收集器種,新生代空間都被劃分紅一個Eden和2個Survivor。一塊標記爲From Survivor空,另外一塊標記爲To Survivor空間。
Eden空間時分配新Java對象的空間,例如一個Java程序中有下面的語句:
Map<String, Long> map = new HashMap<String, Long>();
這行語句會在Eden空間分配一個新的HashMap對象,HashMap構造器中的對象也會保存在Eden空間中,當Eden空間被填滿時就會發生Minor GC。活躍對象會從Eden空間複製到標記爲To的Survivor空間,同時FromSurvivor空間中存活下來的對象也會複製到To Survivor空間中。一旦完成Minor GC, Eden空間會清空,From Survivor空間也變爲空,而To Survivor空間中保存了還活躍的對象。以後,Survivor空間將互相交換標記爲下一次的Minor GC作準備。如今已清空的From Survivor空間換上了To標示,而To Survivor空間換成From標示。所以,Minor GC結束時,Eden空間和一塊Survivor空間變爲空,另外一塊Survivor空間中保存着經歷了上次Minor GC存活下來的活躍對象。
若是Minor GC時,To Survivor空間不足以容納全部從Eden空間和From Survivor空間中複製過來的活躍對象,超出的部分會提高至老年代空間。
調整Survivor空間的小讓其有足夠的空間容納存活對象足夠長的時間,知道幾個週期以後對象老化,就能避免發生Survivor空間溢出,有效的老化方法可使老年代中只保存長期活躍的對象。老化是保持對象在新生代中直到它們變得不可達的一種方法,這樣作的目的是將老年代空間保留下來用於保存長期活躍的對象。
-XX:SurvivorRatio=<ratio>標示單個Survivor空間同Eden空間的大小比率。
Survivor空間的計算公式爲:
Survivor空間的大小 = -Xmn<value> / (-XX:SurvivorRatio=<ratio> + 2)
調整Survivor空間容量一個英國謹記於心的重要原則:調整Survivor空間容量時,若是新生代空間大小不變,增大Survivor空間會減小Eden空間;而減小Eden空間會增大Minor GC的頻率。
所以,爲了同時知足應用程序Minor GC頻率的要求,就須要增大當前新生代空間的大小:即增大Survivor空間大小時,Eden空間的大小應該保持不變。換句話說,每當Survivor空間增長時,新生代空間都應該增大,才能保持Eden空間不變。保持Eden空間大小恆定,Minor GC的頻率就不會因爲Survivor空間增大而發生變化。
經過-XX:+PrintTenuringDistribution選項輸出中的全部對象年齡的總大小以及目標生存空間大小能夠計算出應用程序須要的Survivor空間大小。
目標Survivor空間佔用時VM嘗試在Minor GC以後仍然維持的Survivor空間佔用。經過VM命令行選項-XX:TargetSurvivorRatio=<percent>能夠對該值進行調整。經過命令行選項指定的參數其實是Survivor空間佔用的百分比而不是一個比率,默認值爲50%。
HotSpot VM團隊對不一樣類型的應用程序進行了大量的測試,結果代表50%的目標Survivor空間佔用能適應大多數的應用程序。這是由於它能應對Minor GC時存活對象的急劇增長。極少須要對目標Survivor空間佔用進行調優的狀況,可是若是應用程序有一個相對穩定的分配速率能夠考慮提升目標Survivor空間佔用到80~90。這樣能夠減小用於老化對象的Survivor空間的數量。
爲了對Survivor空間作更細緻的調整,優化新生代堆的大小,須要監控晉升閾值。晉升閾值決定了對象在新生代Survivor空間中保留的時間。
晉升意味着對象提高至老年代空間或者說,晉升閾值就是對象的年齡。一個對象的年齡就是它所經.歷的Minor GC次數。對象首次分配時,它的年齡是0。下一次Minor GC以後,若是該對象還在新生代,其年齡變爲1。以此類推,新生代空間中年齡大於HotSpot VM計算出的晉升閾值的對象都會被提高到老年代空間,換句話說,晉升閾值決定了對象在新生代中保持(或老化)的時間。
注:新生代中的有效對象老化能夠避免將不成熟的對象提高到老年代空間,減小了老年代空間的佔用率增加。
可使用HotSpot VM的命令行選項-XX:MaxTenuringThreshold=<n>指定最大晉升閾值。
若是值設置的過小:可能形成分配的對象在幾回Minor GC以後重新生代提高到老年代,形成老年代空間的迅速增長,引發頻繁的Full GC;
若是值設置的太大:可能形成對象長期存在於Survivor空間,直到最後溢出,一旦發生溢出,對象將被所有提高至老年代,再也不依據其實際年齡進行提高,這樣會形成短時間存在對象在長期存在對象以前被提高到老年代,嚴重影響對象老化機制的有效性。
請注意:最大晉升閾值(-XX:MaxTenuringThreshold=<n>)與內部計算出的晉升閾值相混淆。
使用命令行選項-XX:+PrintTenuringDistribution能夠監控晉升的分佈或對象年齡分佈。並以此爲依據肯定最優的最大晉升閾值。
在輸出中,須要關注的是隨着對象年齡的增長,各對象年齡上字節數減小的狀況,以及VM計算出的晉升閾值是否等於或接近設置的最大晉升閾值。
實例:
最大晉升閾值爲15,VM內部計算出的晉升閾值爲1。Desired survivor size是Survivor空間的大小乘以目標存活率獲得的空間大小。目標存活率是VM預計目標空間在Survivor空間中佔用的百分比。每一個年齡的對象及其佔用的空間大小單獨列爲一行,本例中,年齡爲1的對象大小爲1669048節。同時在每一行中會列出對象總的大小(字節數)。若是出現多年齡行的狀況,總大小是該年齡行及其以前全部對象大小的累積之和。
本例中,指望Survivor空間大小爲8388608遠小於存活對象大小16690480,致使Survivor空間溢出,即最終Minor GC將一些對象提高至老年代。Survivor空間溢出代表Survivor空間太小。
減小最差狀況的延遲並最小化最差延遲發生的頻率,這一步的目標是維持空閒老年代空間的恆定,並由此避免發生STW壓縮式垃圾收集。
STW壓縮式垃圾手機是引入延遲的最大的垃圾手機。在一些應用中,這多是沒法徹底避免的,可是咱們能夠下降它們發生的頻率。
成功的CMS收集器調優要能以對象重新生代提高到老年代的同等速度對老年代中的對象進行垃圾手機。達不到這個標準則稱爲「失速」失速的結果就會發生STW壓縮式垃圾收集。避免失速的關鍵是要結合足夠大的老年代空間和足夠快地初始化CMS垃圾收集週期,讓它以比提高速率更快的速度回收空間。
碰到STW壓縮式垃圾收集,能夠嘗試調節CMS週期啓動的時間,CMS中發生的STW壓縮式垃圾收集的垃圾收集日誌中能夠經過查找併發模式失效定位(Concurrent Mode Failure)。
能夠經過下面的命令行通知VM在更早的時間啓動CMS垃圾收集週期:-XX:CMSInitiatingOccupancyFraction=<percent>
設定的值是CMS垃圾收集週期在老年代空間佔用達到多少百分比時啓動。好比,CMS週期在老年代空間佔用達到65%開始,能夠設置-XX:CMSInitiatingOccupancyFraction=65。
另外一個-XX:+UseCMSInitiatingOccupancyOnly告訴VM老是使用-XX:CMSInitiatingOccupancyFraction設定的值做爲啓動CMS週期的老年代空間佔用閾值。不適用UseCMSInitiatingOccpancyOnly,VM僅僅在啓動的第一個CMS週期裏使用CMSInitiatingOccupancyFraction設定的值做爲佔用比率,以後的週期中又轉向自適應地啓動CMS週期,即第一次CMS週期以後再也不使用CMSInitiatingOccupancyFraction設定的值。
使用CMS時,若是觀察到由顯式調用System.gc()觸發的Full GC,有2種處理的方法。
(1)可使用以下的VM命令行選項,-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses。
(2)頁可使用下面的命令行通知VM忽略顯式的System.gc()調用:-XX:+DisbaleExplicitGC
注意:使用這個命令行選項也會致使其餘VM的垃圾收集器忽略顯式的System.gc()調用。
CMS週期中有2個階段是STW的階段,處於這2個階段(初始標記和從新標記)的應用程序線程會被阻塞。雖然初始標記階段是單線程的,卻極少佔很長時間,一般狀況下遠小於其餘的垃圾收集停頓。從新標記階段是多線程的。能夠經過VM命令行選項控制從新標記階段使用的線程數-XX:ParallelGCTheads=<n>,若是Runtime.availableProcessors()的返回值<=8,-XX:ParallelGCThreads默認等於這個值;不然,該默認值爲8 + (Runtime.availableProcessors() - 8)* 5 / 8。
從新標記階段的持續時間在某些時候能夠經過下面的選項設置:-XX:+CMSScavengeBeforeRemark該選項強制VM在進入CMS從新標記階段以前先進行一次Minor GC。從新標記以前的MInor GC經過減小老年代空間的新生代對象數目,將從新標記階段的工做量減到了最少。
使用CMS收集器時,爲了得到更大的吞吐量性提高你須要使用一系誒配置選項。
以上任何一個選項或幾個選項的組合均可以減小垃圾收集器消耗的CPU週期數,從而將更多的CPU週期用於執行應用程序。
指導原則:CMS包括Minor GC所帶來的開銷應該小於10%,你可能將這個值減小到1%~3%,一般狀況下,若是當前觀察到CMS垃圾收集的開銷在3%或更少,經過調優吞吐量性能提高的空間就極其有限了。
Java性能優化權威指南