paulwong

          JVM實用參數(shù)-6 吞吐量收集器

          在實踐中我們發(fā)現(xiàn)對于大多數(shù)的應(yīng)用領(lǐng)域,評估一個垃圾收集(GC)算法如何根據(jù)如下兩個標準:  
          1. 吞吐量越高算法越好
          2. 暫停時間越短算法越好
          首先讓我們來明確垃圾收集(GC)中的兩個術(shù)語:吞吐量(throughput)和暫停時間(pause times)。 JVM在專門的線程(GC threads)中執(zhí)行GC。 只要GC線程是活動的,它們將與應(yīng)用程序線程(application threads)爭用當前可用CPU的時鐘周期。 簡單點來說,吞吐量是指應(yīng)用程序線程用時占程序總用時的比例。 例如,吞吐量99/100意味著100秒的程序執(zhí)行時間應(yīng)用程序線程運行了99秒, 而在這一時間段內(nèi)GC線程只運行了1秒。

          術(shù)語”暫停時間”是指一個時間段內(nèi)應(yīng)用程序線程讓與GC線程執(zhí)行而完全暫停。 例如,GC期間100毫秒的暫停時間意味著在這100毫秒期間內(nèi)沒有應(yīng)用程序線程是活動的。 如果說一個正在運行的應(yīng)用程序有100毫秒的“平均暫停時間”,那么就是說該應(yīng)用程序所有的暫停時間平均長度為100毫秒。 同樣,100毫秒的“最大暫停時間”是指該應(yīng)用程序所有的暫停時間最大不超過100毫秒。


          吞吐量 VS 暫停時間

          高吞吐量最好因為這會讓應(yīng)用程序的最終用戶感覺只有應(yīng)用程序線程在做“生產(chǎn)性”工作。 直覺上,吞吐量越高程序運行越快。 低暫停時間最好因為從最終用戶的角度來看不管是GC還是其他原因?qū)е乱粋€應(yīng)用被掛起始終是不好的。 這取決于應(yīng)用程序的類型,有時候甚至短暫的200毫秒暫停都可能打斷終端用戶體驗。

          因此,具有低的最大暫停時間是非常重要的,特別是對于一個交互式應(yīng)用程序。 不幸的是”高吞吐量”和”低暫停時間”是一對相互競爭的目標(矛盾)。這樣想想看,為了清晰起見簡化一下:GC需要一定的前提條件以便安全地運行。 例如,必須保證應(yīng)用程序線程在GC線程試圖確定哪些對象仍然被引用和哪些沒有被引用的時候不修改對象的狀態(tài)。

          為此,應(yīng)用程序在GC期間必須停止(或者僅在GC的特定階段,這取決于所使用的算法)。 然而這會增加額外的線程調(diào)度開銷:直接開銷是上下文切換,間接開銷是因為緩存的影響。 加上JVM內(nèi)部安全措施的開銷,這意味著GC及隨之而來的不可忽略的開銷,將增加GC線程執(zhí)行實際工作的時間。

          因此我們可以通過盡可能少運行GC來最大化吞吐量,例如,只有在不可避免的時候進行GC,來節(jié)省所有與它相關(guān)的開銷。 然而,僅僅偶爾運行GC意味著每當GC運行時將有許多工作要做,因為在此期間積累在堆中的對象數(shù)量很高。 單個GC需要花更多時間來完成, 從而導致更高的平均和最大暫停時間。 因此,考慮到低暫停時間,最好頻繁地運行GC以便更快速地完成。 這反過來又增加了開銷并導致吞吐量下降,我們又回到了起點。

          綜上所述,在設(shè)計(或使用)GC算法時​​,我們必須確定我們的目標:一個GC算法​​只可能針對兩個目標之一(即只專注于最大吞吐量或最小暫停時間),或嘗試找到一個二者的折衷。


          HotSpot虛擬機上的垃圾收集

          該系列的第五部分我們已經(jīng)討論過年輕代的垃圾收集器。 對于年老代,HotSpot虛擬機提供兩類垃圾收集算法(除了新的G1垃圾收集算法),第一類算法試圖最大限度地提高吞吐量,而第二類算法試圖最小化暫停時間。 今天我們的重點是第一類,”面向吞吐量”的垃圾收集算法。

          我們希望把重點放在JVM配置參數(shù)上,所以我只會簡要概述HotSpot提供的面向吞吐量(throughput-oriented)垃圾收集算法。 當年老代中由于缺乏空間導致對象分配失敗時會觸發(fā)垃圾收集器(事實上,”分配”的通常是指從年輕代提升到年老代的對象)。

          從所謂的”GC根”(GC roots)開始,搜索堆中的可達對象并將其標記為活著的,之后,垃圾收集器將活著的對象移到年老代的一塊無碎片(non-fragmented)內(nèi)存塊中,并標記剩余的內(nèi)存空間是空閑的。 也就是說,我們不像復制策略那樣移到一個不同的堆區(qū)域,像年輕代垃圾收集算法所做的那樣。

          相反地,我們把所有的對象放在一個堆區(qū)域中,從而對該堆區(qū)域進行碎片整理。 垃圾收集器使用一個或多個線程來執(zhí)行垃圾收集。 當使用多個線程時,算法的不同步驟被分解,使得每個收集線程大多時候工作在自己的區(qū)域而不干擾其他線程。 在垃圾收集期間,所有的應(yīng)用程序線程暫停,只有垃圾收集完成之后才會重新開始。

          現(xiàn)在讓我們來看看跟面向吞吐量垃圾收集算法有關(guān)的重要JVM配置參數(shù)。


          -XX:+UseSerialGC

          我們使用該標志來激活串行垃圾收集器,例如單線程面向吞吐量垃圾收集器。 無論年輕代還是年老代都將只有一個線程執(zhí)行垃圾收集。 該標志被推薦用于只有單個可用處理器核心的JVM。 在這種情況下,使用多個垃圾收集線程甚至會適得其反,因為這些線程將爭用CPU資源,造成同步開銷,卻從未真正并行運行。


          -XX:+UseParallelGC

          有了這個標志,我們告訴JVM使用多線程并行執(zhí)行年輕代垃圾收集。 在我看來,Java 6中不應(yīng)該使用該標志因為-XX:+UseParallelOldGC顯然更合適。 需要注意的是Java 7中該情況改變了一點(詳見本概述),就是-XX:+UseParallelGC能達到-XX:+UseParallelOldGC一樣的效果。


          -XX:+UseParallelOldGC

          該標志的命名有點不巧,因為”老”聽起來像”過時”。 然而,”老”實際上是指年老代,這也解釋了為什么-XX:+UseParallelOldGC要優(yōu)于-XX:+UseParallelGC:除了激活年輕代并行垃圾收集,也激活了年老代并行垃圾收集。 當期望高吞吐量,并且JVM有兩個或更多可用處理器核心時,我建議使用該標志。
          作為旁注,HotSpot的并行面向吞吐量垃圾收集算法通常稱為”吞吐量收集器”,因為它們旨在通過并行執(zhí)行來提高吞吐量。


          -XX:ParallelGCThreads

          通過-XX:ParallelGCThreads=<value>我們可以指定并行垃圾收集的線程數(shù)量。 例如,-XX:ParallelGCThreads=6表示每次并行垃圾收集將有6個線程執(zhí)行。 如果不明確設(shè)置該標志,虛擬機將使用基于可用(虛擬)處理器數(shù)量計算的默認值。 決定因素是由Java Runtime。availableProcessors()方法的返回值N,如果N<=8,并行垃圾收集器將使用N個垃圾收集線程,如果N>8個可用處理器,垃圾收集線程數(shù)量應(yīng)為3+5N/8。

          當JVM獨占地使用系統(tǒng)和處理器時使用默認設(shè)置更有意義。 但是,如果有多個JVM(或其他耗CPU的系統(tǒng))在同一臺機器上運行,我們應(yīng)該使用-XX:ParallelGCThreads來減少垃圾收集線程數(shù)到一個適當?shù)闹怠?例如,如果4個以服務(wù)器方式運行的JVM同時跑在在一個具有16核處理器的機器上,設(shè)置-XX:ParallelGCThreads=4是明智的,它能使不同JVM的垃圾收集器不會相互干擾。


          -XX:-UseAdaptiveSizePolicy

          吞吐量垃圾收集器提供了一個有趣的(但常見,至少在現(xiàn)代JVM上)機制以提高垃圾收集配置的用戶友好性。 這種機制被看做是HotSpot在Java 5中引入的”人體工程學”概念的一部分。 通過人體工程學,垃圾收集器能將堆大小動態(tài)變動像GC設(shè)置一樣應(yīng)用到不同的堆區(qū)域,只要有證據(jù)表明這些變動將能提高GC性能。 “提高GC性能”的確切含義可以由用戶通過-XX:GCTimeRatio和-XX:MaxGCPauseMillis(見下文)標記來指定。

          重要的是要知道人體工程學是默認激活的。 這很好,因為自適應(yīng)行為是JVM最大優(yōu)勢之一。 不過,有時我們需要非常清楚對于特定應(yīng)用什么樣的設(shè)置是最合適的,在這些情況下,我們可能不希望JVM混亂我們的設(shè)置。 每當我們發(fā)現(xiàn)處于這種情況時,我們可以考慮通過-XX:-UseAdaptiveSizePolicy停用一些人體工程學。


          -XX:GCTimeRatio

          通過-XX:GCTimeRatio=<value>我們告訴JVM吞吐量要達到的目標值。 更準確地說,-XX:GCTimeRatio=N指定目標應(yīng)用程序線程的執(zhí)行時間(與總的程序執(zhí)行時間)達到N/(N+1)的目標比值。 例如,通過-XX:GCTimeRatio=9我們要求應(yīng)用程序線程在整個執(zhí)行時間中至少9/10是活動的(因此,GC線程占用其余1/10)。 基于運行時的測量,JVM將會嘗試修改堆和GC設(shè)置以期達到目標吞吐量。 -XX:GCTimeRatio的默認值是99,也就是說,應(yīng)用程序線程應(yīng)該運行至少99%的總執(zhí)行時間。


          -XX:MaxGCPauseMillis

          通過-XX:GCTimeRatio=<value>告訴JVM最大暫停時間的目標值(以毫秒為單位)。 在運行時,吞吐量收集器計算在暫停期間觀察到的統(tǒng)計數(shù)據(jù)(加權(quán)平均和標準偏差)。 如果統(tǒng)計表明正在經(jīng)歷的暫停其時間存在超過目標值的風險時,JVM會修改堆和GC設(shè)置以降低它們。 需要注意的是,年輕代和年老代垃圾收集的統(tǒng)計數(shù)據(jù)是分開計算的,還要注意,默認情況下,最大暫停時間沒有被設(shè)置。

          如果最大暫停時間和最小吞吐量同時設(shè)置了目標值,實現(xiàn)最大暫停時間目標具有更高的優(yōu)先級。 當然,無法保證JVM將一定能達到任一目標,即使它會努力去做。 最后,一切都取決于手頭應(yīng)用程序的行為。

          當設(shè)置最大暫停時間目標時,我們應(yīng)注意不要選擇太小的值。 正如我們現(xiàn)在所知道的,為了保持低暫停時間,JVM需要增加GC次數(shù),那樣可能會嚴重影響可達到的吞吐量。 這就是為什么對于要求低暫停時間作為主要目標的應(yīng)用程序(大多數(shù)是Web應(yīng)用程序),我會建議不要使用吞吐量收集器,而是選擇CMS收集器。 CMS收集器是本系列下一部分的主題。

          posted on 2014-06-16 17:26 paulwong 閱讀(6694) 評論(0)  編輯  收藏 所屬分類: J2SE性能優(yōu)化

          主站蜘蛛池模板: 枣阳市| 永兴县| 万宁市| 马鞍山市| 肇东市| 顺义区| 锦屏县| 扶余县| 紫金县| 琼中| 夏邑县| 上高县| 大新县| 犍为县| 新建县| 新余市| 漳州市| 庆云县| 崇阳县| 荆门市| 台安县| 铁力市| 阿拉善盟| 鄂伦春自治旗| 阜平县| 林周县| 如皋市| 甘南县| 洱源县| 上蔡县| 论坛| 从江县| 顺昌县| 凤冈县| 都昌县| 得荣县| 山丹县| 鹿泉市| 杨浦区| 贵港市| 新平|