偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Java SE 6 Hotspot虛擬機(jī)垃圾回收調(diào)優(yōu)

開發(fā) 后端
譯者按:有些術(shù)語實(shí)在很別扭,不過譯者文采有限,沒法找到合適的中文詞匯,不太影響理解,湊合看吧。

譯者按:有些術(shù)語實(shí)在很別扭,不過譯者文采有限,沒法找到合適的中文詞匯,不太影響理解,湊合看吧。

1. 概述

Java 平臺標(biāo)準(zhǔn)版(Java SE™)被廣泛應(yīng)用于各種應(yīng)用,從桌面上的小小的 applet 到大型服務(wù)器上的 Web Service 無處不在。為了支持各種不同的部署場景,Java HotSpot™ 虛擬機(jī)提供了多種垃圾回收器,每種都為滿足不同的需求而設(shè)定。這是也為了滿足大大小小不同應(yīng)用需求的一部分。不過,那些需要高性能應(yīng)用的用戶、開發(fā)者和管理員們也被選擇適合他們應(yīng)用的恰當(dāng)?shù)睦厥掌鞯姆爆嵗_著。取消這些額外操作的重要一步是在 J2SE™ 5.0 中作出的:垃圾回收器會根據(jù)應(yīng)用運(yùn)行的計算機(jī)類型而作出選擇。

這個垃圾回收器的“更好的選擇”總的說是一種進(jìn)步,不過,這并不意味著對所有的應(yīng)用這都是最好的選擇。對于有極端的性能或其他需求的用戶,仍需要顯式地指定垃圾回收器,并調(diào)優(yōu)某些參數(shù),以達(dá)到滿意的性能。本文就為這些需求提供了一些相關(guān)信息。首先,本文會基于串行的 stop-the-world 垃圾回收器來介紹垃圾回收器的一般性特征和基本調(diào)優(yōu)開關(guān)。接下來會介紹其他垃圾回收器的特點(diǎn)和如何選擇一個垃圾回收器。

何時選擇垃圾回收器?對于一些應(yīng)用,這個答案可能是“永遠(yuǎn)不”。也就是說,在有低頻率、短時的垃圾收集器造成的停頓的情況下,大部分程序都運(yùn)行良好。不過,這并不適用于很多程序,特別是那些處理大量數(shù)據(jù)(若干GB)、很多線程和需要處理很多事務(wù)的情況。

Amdahl 觀察到,大部分工作負(fù)載并不能被很好的并行化;有部分情況下總是會被順序執(zhí)行,無法從并行化中獲益。這對 Java™ 平臺也是如此。特別的,在 J2SE 1.4 以前,Sun Java 平臺的虛擬機(jī)并不支持并行垃圾回收,這樣,在多處理器系統(tǒng)中,垃圾回收會對并行應(yīng)用產(chǎn)生嚴(yán)重影響。

下圖顯示了一個除了垃圾回收以外均為完美可伸縮的理想系統(tǒng)的性能曲線。紅色曲線是一個在但處理器系統(tǒng)中會花費(fèi) 1% 的時間在垃圾回收上的程序。它在 32 處理器的系統(tǒng)中,將損失 20% 的吞吐量。而一個花費(fèi) 10% 時間在垃圾回收上的應(yīng)用(不考慮單處理器系統(tǒng)中額外的垃圾回收時間)在系統(tǒng)擴(kuò)張到 32 處理器系統(tǒng)中時,會損失超過 75% 的吞吐量 。

這意味著在小型開發(fā)系統(tǒng)中微不足道的速度問題當(dāng)擴(kuò)張到大規(guī)模系統(tǒng)中就可能成為嚴(yán)重的性能瓶頸。從另一個角度看,減少這樣的性能瓶頸的小改動就可以獲得很大的性能收益。對足夠大規(guī)模的系統(tǒng),選擇合適的垃圾收集器并進(jìn)行必要調(diào)優(yōu)是絕對值得的。

對于大多數(shù)“小”應(yīng)用(在現(xiàn)代處理器上大約需要100MB堆內(nèi)存的應(yīng)用)來說通常是足夠的。其他垃圾收集器會帶來額外的負(fù)載或復(fù)雜性,這回讓系統(tǒng)的某些行為付出一定的代價。如果一個應(yīng)用不需要一個垃圾收集器的某個功能。那么就使用串行的垃圾收集器好了。一個不應(yīng)該使用串行垃圾收集器場景是一個超多線程的大程序運(yùn)行在一個大型的、有大量內(nèi)存和兩個或多個處理器的系統(tǒng)中。當(dāng)應(yīng)用運(yùn)行在這些服務(wù)器級的計算機(jī)上的時候,并行垃圾收集器會被缺省選擇(參見下面的功效學(xué) )。

本文以 Solaris™ 操作系統(tǒng)(SPARC(R) 平臺版本)中的 Java SE 6 作為參考。不過,文中所述的概念和建議適用于所有支持的平臺,包括 Linux, Microsoft Windows 和 Solaris 操作系統(tǒng)(x86 平臺版本)。此外,文中的命令行參數(shù)也對所有平臺有效,雖然它們的缺省值在各個平臺可能有所不同。

2. 功效學(xué)(Ergonomics)

“功效學(xué)”是一個 J2SE 5.0 引入的概念。引入功效學(xué)概念是為了通過不設(shè)置或設(shè)置很少的幾個命令行參數(shù)的情況下提供更好的性能,這些參數(shù)包括:

  • 垃圾收集器,
  • 堆尺寸,
  • 和運(yùn)行時編譯器

這里的參數(shù)選擇假定應(yīng)用所運(yùn)行的主機(jī)類型和應(yīng)用的類型一致(也就是說,大型應(yīng)用運(yùn)行在大型的機(jī)器上)。這些選項(xiàng)簡化了垃圾回收的調(diào)優(yōu)。選擇并行垃圾回收器,用戶可以指定應(yīng)用的最大中斷時間和希望的吞吐量。這和指定堆大小來調(diào)優(yōu)性能是相對應(yīng)的。最常用的功效學(xué)相關(guān)的內(nèi)容在可以參考 “Ergonomics in the 5.0 Java Virtual Machine” 這篇文章。建議在嘗試本文提到的細(xì)節(jié)配置之前嘗試該文章中介紹的功效學(xué)手段。

本文中的功效學(xué)特性被作為并行垃圾回收器的自適應(yīng)尺寸策略的一部分。這包括指定垃圾回收性能的目標(biāo)和性能調(diào)優(yōu)的一些附加選項(xiàng)。

3.代

J2SE 平臺的優(yōu)勢之一是它將內(nèi)存分配、垃圾回收這些繁復(fù)的細(xì)節(jié)屏蔽了起來。然而,一旦垃圾回收成為主要的瓶頸,那么理解一下這些隱藏在背后的細(xì)節(jié)就變得有必要了。垃圾回收器對應(yīng)用程序?qū)ο蟮氖褂梅绞竭M(jìn)行判斷,這個判斷會反映在可調(diào)優(yōu)參數(shù)中,他們可以被調(diào)整,以提高性能而不犧牲掉抽象性。

當(dāng)一個對象不再可能被從其他任何地方訪問到的時候就會被認(rèn)為是垃圾了。最直接的垃圾回收算法就是簡單地迭代所有可找到的對象。任何沒有被跌帶到的對象都可以被認(rèn)為是垃圾了。這個方法的用時和活著的對象數(shù)量成正比,這對于那些維護(hù)著大量活數(shù)據(jù)的程序來說是不可接受的。

從 J2SE 1.2 開始,虛擬機(jī)就引入了各種不同的垃圾回收算法,這些算法都使用分代垃圾收集。盡管原生的垃圾回收會檢查堆中的所有活著的對象,分代垃圾收集采用了很多觀測到的大部分應(yīng)用程序的經(jīng)驗(yàn)特征,用來最小化發(fā)現(xiàn)廢棄的對象的工作量。最重要的經(jīng)驗(yàn)特征是 weak generational 假設(shè),該假設(shè)認(rèn)為大部分對象都只存活一少段時間。

下圖中的藍(lán)色區(qū)域是對象生存期的典型分布。橫軸是對象被分配后的生存期??v軸方向計算的字節(jié)數(shù)是相應(yīng)生存期的對象的總字節(jié)數(shù)。左側(cè)的尖峰表明,對象在分配之后不久就被廢棄了。比如,迭代器對象常常只會在一個循環(huán)中被用到。

當(dāng)然,有些對象確實(shí)活得要長一些,于是,分布曲線延伸到了右邊。比如,典型情況下,有些對象在初始化的時候被創(chuàng)建,并一直存活到進(jìn)程結(jié)束。在這兩種極限情況之間,那些對象活的時間也是中等的,在圖中表現(xiàn)初來的就是從開始的峰值泄漏初來的藍(lán)色區(qū)域。有些應(yīng)用可能會有看起來十分不同的分布曲線,不過絕大多數(shù)的進(jìn)程都是這個常見的形狀。大部分對象都會“英年早逝”這個事實(shí)讓高效的垃圾收集變得具有可能性了。

為了為這樣的應(yīng)用環(huán)境優(yōu)化,內(nèi)存被按照“代” (generation)進(jìn)行管理,或者說,內(nèi)存池中存放不同年齡的對象。當(dāng)一個年齡斷被填滿后,就對該代的垃圾進(jìn)行回收。在內(nèi)存池中的大部分對象都是年輕的對象(年輕的代),而大部分對象也會在年輕的時候就成為垃圾。當(dāng)年輕代被填滿的時候,會導(dǎo)致一次“小回收”(譯注:原文minor,似乎“未成年”更貼切一些,不過咱們讀起來會很別扭),這里只有年輕代的對象惠北回收,而其他年齡斷的垃圾則不與理會。該回收算法的成本是,一階情況下,正比于被回收的活的對象的數(shù)量;年輕代因?yàn)闈M是死對象,所以回收非常迅速。而在“小回收”中存活下來的對象于是乎就會被轉(zhuǎn)移到所謂的年老代(tenured generation)。最終,當(dāng)年老代被填滿而需要回收的時候,就會導(dǎo)致一次主回收,這時整個堆都會被回收。主回收通常會運(yùn)行锝比小回收慢很多,因?yàn)榇罅康膶ο蠖紩惶幚怼?/p>

如上文記述,對不同的應(yīng)用,“工效學(xué)”會動態(tài)選擇垃圾收集器來提供較好的性能。串行垃圾收集器用于哪些數(shù)據(jù)量比較小的程序,而且它的缺省參數(shù)也讓大多數(shù)小程序能夠高效工作。而大吞吐量垃圾收集器用于那些有中到大數(shù)據(jù)量的數(shù)據(jù)集。工效學(xué)選擇的堆尺寸參數(shù)和自適應(yīng)尺寸策略用于為服務(wù)器提供更好的性能。這些選擇的大多數(shù)而不是所有的情況下工作得很不錯。這就引出了本文的核心宗旨:

如果垃圾收集器成為了瓶頸,你可能不得不調(diào)整整個堆的大小乃至每個代的尺寸。檢查垃圾收集器的詳細(xì)輸出,然后檢查垃圾收集器對你關(guān)注的各個性能指標(biāo)的影響。

(并行垃圾收集器之外的)缺省的代排布大概就是這樣的。

初始化的時候,最大的地址空間虛擬地保留住而沒有分配出去,直到真的需要的時候?yàn)橹?。整個保留的對象地址空間被分給了年輕的和年老的代。

年輕代包括“伊甸園”和兩個幸存者空間。大部分對象最初在伊甸園里被分配出來。一個幸存者空間在任意時刻都是空的,作為伊甸園中的活對象的目的地,另一個是用于下一次收集。對象在幸存者空間之間停留到足夠老之后,就會被復(fù)制到年老代去了。

另一個和年老代有密切關(guān)系的代是永久的(permanent)代,這里保存著虛擬機(jī)需要的用來描述那些 Java 語言層面沒有等價物的對象。比如,那些描述類和方法的的對象就存放在永久代。

3.1 性能考慮

對于垃圾回收的性能,主要有兩種量度方法:

  1. 吞吐量。吞吐量是在一段足夠長的時間中,沒有花費(fèi)在垃圾回收上的時間占總時間的百分比。吞吐量包含了花在空間費(fèi)配上的時間(不過空間分配速度的調(diào)優(yōu)一般是沒有必要的)。
  2. 延時。延時是由于等待垃圾回收而導(dǎo)致的程序沒有響應(yīng)的時間。

不同的用戶對垃圾收集有不同的需求。比如,對于一個web server而言,吞吐量是合理的量度,因?yàn)槔占瘞淼亩虝r時延是可以容忍的,或者說是很容易就被網(wǎng)絡(luò)時延所掩蓋了。不過,對于交互的圖形界面程序而言,極短的停頓都會影響用戶的使用體驗(yàn)。

有些用戶對其他的因素很敏感。Footprint是一個進(jìn)程的工作集,由頁和cache line來量度。對于內(nèi)存相對于進(jìn)程數(shù)量很有限的系統(tǒng)而言。Footprint會影響到程序的可伸縮性。Promptness是對象死掉和該塊內(nèi)存重新可用之間的時間間隔的量度,這是分布式系統(tǒng)的一個重要考慮因素,包括遠(yuǎn)程方法調(diào)用(RMI)。

總的說,一個特定的代的尺寸選擇是上述這些因素之間的權(quán)衡的結(jié)果。比如,一個非常大的年輕代的大小可以最大化吞吐律,但會以Footprint、 Promptness和延時作為代價。而年輕代延時可以通過縮小該代的大小來達(dá)到最小化,但同樣會損失吞吐量。近似地,調(diào)整一個代的尺寸不會影響到其他代的垃圾收集頻率和時延。

沒有一個簡單的方法來設(shè)置代的尺寸。最好的選擇由程序使用內(nèi)存的方式和用戶的需求來決定。這樣,虛擬機(jī)對垃圾收集器的選擇并不總是最優(yōu)的,而且可以通過后面介紹的命令行參數(shù)來調(diào)整。

3.2 測量

使用應(yīng)用特定的量度,吞吐量和footprint很容易被測量。例如,web服務(wù)器的吞吐量可以使用一個客戶端負(fù)載生成器來測量,而該服務(wù)器的 footprint 則可以在 Solaris 操作系統(tǒng)中使用 pmap 命令來測量。另一方面,垃圾收集導(dǎo)致的時延可以方便地通過監(jiān)測虛擬機(jī)自己的診斷輸出來估算出來。

命令行參數(shù) -verbos:gc 可以送出每一次垃圾收集時的堆和垃圾收集信息。比如,這是一個大型服務(wù)器應(yīng)用的輸出:

  1. [GC 325407K->83000K(776768K), 0.2300771 secs]  
  2. [GC 325816K->83372K(776768K), 0.2454258 secs]  
  3. [Full GC 267628K->83769K(776768K), 1.8479984 secs] 

這里是兩次小回收和之后的一次主回收。箭頭前后的數(shù)字(比如第一行的325407K->83000K)分別指垃圾回收前后的所有活著的對象占用的空間。在小回收之后,這個尺寸之中仍然包含一些沒有被回收的垃圾(死掉的對象)。這些對象要么存在在年老代中,要么被年老或永久代中的對象所引用。

后面的括號中的數(shù)字(比如第一行中的 (776768K))是全部提交的堆大小,也就是虛擬己不向操作系統(tǒng)申請內(nèi)存的情況下,全部 java 對象可用的存儲空間。注意,這個數(shù)字不包括幸存者空間中的一個,因?yàn)樾掖嬲呖臻g在一個給定時間只有一個可用,同時也不包括永久代的空間,這里面是虛擬機(jī)使用的元數(shù)據(jù)。

最后一個數(shù)字(比如 0.2300771 secs)是垃圾收集所用的時間;這個例子里大約是四分之一秒。

第三行中主垃圾回收的格式也是類似的。

-verbos:gc 輸出的格式可能在將來的版本里有所改變。

通過-XX:+PrintGCDetails參數(shù)可以查看更多垃圾回收相關(guān)的信息。下面是串行垃圾收集器使用該參數(shù)打印出來的信息。

  1. [GC [DefNew: 64575K->959K(64576K), 0.0457646 secs] 196016K->133633K(261184K), 0.0459067 secs] 

這個信息顯示,這次小回收收回了 98% 的 DefNew 年輕代的數(shù)據(jù),64575K->959K(64576K) 并在其上消耗了 0.0457646 secs(大約45毫秒)。

整個堆的占用率下降了大約51% 196016K->133633K(261184K),而且通過最終的時間 0.0459067 secs 顯示在垃圾收集中有輕微的開銷(在年輕代之外的時間)。

選項(xiàng)-XX:+PrintGCTimeStamps會提供每次回收開始時間的時間戳。這對于查看垃圾回收頻率非常有用。

111.042: [GC 111.042: [DefNew: 8128K->8128K(8128K), 0.0000505 secs] 111.042: [Tenured: 18154K->2311K(24576K), 0.1290354 secs]  26282K->2311K(32704K), 0.1293306 secs]

如上,垃圾回收在程序運(yùn)行后111秒開始。小回收同時啟動。信息中還顯示了主回收中的年老代的垃圾回收信息。年老代的空間使用率下降了大約 10% 18154K->2311K(24576K) ,用時 0.1290354(大約130毫秒)。

和 -verbose:gc 一樣,-XX:+PrintGCDetails 的輸出格式在將來的版本里也可能會有所變動。

#p#

4. 代的尺寸

很多參數(shù)會應(yīng)想到代的尺寸。下圖是堆中的提交空間和虛擬空間的差別。虛擬機(jī)初始化的時候,整個堆空間都是保留的。保留空間可以通過參數(shù) -Xmx 指定。如果-Xms參數(shù)小于-Xmx參數(shù),那么不是所有的保留空間都會立刻提交到虛擬機(jī)之中。未提交的空間在途中標(biāo)記為 virtual。堆的不同部分(永久時間段、年老時間段和年輕時間段)可以按需生長到虛擬空間的限制為止。

一些參數(shù)可以調(diào)整堆的不同部分的比例,比如參數(shù)NewRatio指定年老代對年輕代的比例。這些參數(shù)將在下面討論。

4.1 全部堆

注意,下面的關(guān)于堆的生長、收縮和缺省堆大小都不適用于并行垃圾收集器,并行垃圾收集器請參考相關(guān)章節(jié)。不過,用于控制整個堆大小和代尺寸的參數(shù)對并行垃圾收集器都是適用的。

因?yàn)槔占前l(fā)生在代被填滿的時候,所以,吞吐量反比于可用此內(nèi)存數(shù)量??偪捎脙?nèi)存數(shù)是影響垃圾收集性能的最重要因素。

缺省情況下,虛擬己在每次垃圾收集后增加或減少堆尺寸,來盡量保持可用空間對活的對象之間的比例在一個區(qū)間之內(nèi)。這個目標(biāo)區(qū)間通過參數(shù) -XX:MinHeapFreeRatio=<minimum>和-XX:MaxHeapFreeRatio=<maximum& gt;來設(shè)置,而總的堆大小的界限由-Xms<min>和-Xmx<max>來確定。這些參數(shù)在 32 位 Solaris 系統(tǒng)(SPARC 平臺版本)中的缺省值如下表所示:

  1. Parameter   
  2. Default Value   
  3. MinHeapFreeRatio  
  4. 40  
  5. MaxHeapFreeRatio  
  6. 70  
  7. -Xms  
  8. 3670k  
  9. -Xmx  
  10. 64m 

64位系統(tǒng)中的堆尺寸的參數(shù)會大 30% 左右,這個增長用來補(bǔ)償64位系統(tǒng)中更大的對象所帶來的開銷。

通過設(shè)置這些參數(shù),當(dāng)一個代的可用空間低于 40%,虛擬機(jī)就會把可用內(nèi)存擴(kuò)展到 40%,直到代的最大尺寸。同樣的,如果可用空間超過 70%,代就會被縮小,使得只有 70% 可用空間,直到達(dá)到代最小的空間為止。

大型服務(wù)器程序在使用這些缺省設(shè)置時,經(jīng)常遇到兩種問題。其一是慢啟動問題,初始的堆尺寸過小,經(jīng)常需要經(jīng)歷多次主回收才能達(dá)到穩(wěn)定值。另一個更現(xiàn)實(shí)的問題是,對于大多數(shù)服務(wù)器應(yīng)用來說,這個缺省的最大堆大小太小了。對于服務(wù)器程序而言,設(shè)置的一般原則是:

  • 除非遇到了時延問題,給虛擬機(jī)盡量多的內(nèi)存。缺省尺寸(64MB)通常都太小了。
  • 把-Xms 和 -Xmx 設(shè)置成相同的值,把最重要的尺寸決定從虛擬機(jī)收回來,從而增強(qiáng)可預(yù)見性。
  • 一般地,隨著處理器數(shù)量的增加而增加內(nèi)存,因?yàn)閮?nèi)存分配可以被并行化。

作為參考,有一個單獨(dú)的頁面會介紹各個命令行參數(shù) 。

4.2 年輕代

影響位居次席的是用于年輕代的堆比例。年輕代越大,小回收的次數(shù)也就越少。不過,在一定的堆大小的情況下,年輕代越大,年老代也就越小,這就增加了主回收的頻率。最佳選擇依賴于應(yīng)用中分配的對象的生存期分布。

缺省的,年輕代的尺寸由 NewRatio 控制。比如,設(shè)置-XX:NewRatio=3意味著年輕代和年老代的比例是1:3。換句話說, eden 和幸存者空間的總和是整個堆大小的四分之一。

參數(shù) NewSize 和 MaxNewSize 約束了年輕代的上下界限??梢园堰@兩個參數(shù)設(shè)成相同的值來固定年輕代的大小,設(shè)置 -Xms 和 -Xmx 一樣來設(shè)置堆大小為固定值。這樣可以比使用NewRatio更細(xì)粒度地調(diào)整年輕代的大小。

4.2.1 幸存者空間

如果需要,SurvivorRatio 可以用來調(diào)整幸存者空間的大小,不過這對于性能一般影響不大。比如,-XX:SurvivorRatio=6 會設(shè)置幸存者空間和eden的比例是 1:6。換句話說,每個幸存者空間將是 eden 的六分之一,是整個年輕代空間的八分之一(不是七分之一,因?yàn)橐还灿袃蓚€幸存者空間)。

如果幸存者空間過小的話,拷貝收集到的幸存者將會直接溢出到年老代的空間中去。如果幸存者空間太大的話,他們也就是空著浪費(fèi)掉。每次垃圾收集中,虛擬機(jī)會選擇一個對象在成為年老的之前被復(fù)制的次數(shù)門限。這個門限的設(shè)置會保證幸存者空間是半滿的。命令行參數(shù) -XX:+PrintTenuringDistribution 可以顯示這個門限和年輕代中對象的年齡。這對于觀測應(yīng)用中對象的生存期分布也是有用的。

下面是 SPARC 上的 32 位 Solaris 的各個參數(shù)的缺省值,在其他平臺上可能有所差異。

  1. Default Value   
  2. Parameter   
  3. Client JVM   
  4. Server JVM   
  5. NewRatio  
  6. 8  
  7. 2  
  8. NewSize  
  9. 2228K  
  10. 2228K  
  11. MaxNewSize  
  12. not limited  
  13. not limited  
  14. SurvivorRatio  
  15. 32  
  16. 32 

年輕代的最大尺寸通過最大堆尺寸和 NewRatio 計算而得。所謂的“無限制”的缺省值是說這個計算的值不會受到 MaxNewSize 的約束,除非命令行中指定了這個值。

服務(wù)應(yīng)用的設(shè)置準(zhǔn)則是:

  • 首先確定可以提供給虛擬己的最大堆尺寸。然后根據(jù)性能需求來確定年輕代的尺寸,來找到最佳設(shè)置。
    • 注意:最大堆尺寸一定要小于系統(tǒng)中的內(nèi)存數(shù)量,以防止過多的缺頁錯誤和換頁。
  • 如果總的堆尺寸是確定的,增加年輕代的尺寸就會減少年老代的尺寸。一定要保證年老代的尺寸,使之可以容納所有在應(yīng)用全程都要用到的活對象,并留有一定裕量(10-20%或更多)。
  • 依照上述年老代的約束:
    • 給年輕代分配足夠的內(nèi)存。
    • 如果有多個處理器,那么分配更多的內(nèi)存給年輕代,因?yàn)閮?nèi)存分配可以并行化。

5. 可用的垃圾收集器

到目前為止,我們討論的還都是串行垃圾收集器。不過 Java HotSpot 虛擬機(jī)一共支持了三種不同的收集器,每種提供不同的性能特性。

  1. 串行垃圾收集器使用單線程進(jìn)行所有垃圾收集工作,因?yàn)闆]有線程間通信的開銷,串行垃圾收集器相當(dāng)高效。串行垃圾收集器最適合于單處理器系統(tǒng),因?yàn)樗粫亩嗵幚砥饔布蝎@益,盡管在小數(shù)據(jù)量的應(yīng)用中(不大于100MB的),它對于多處理器系統(tǒng)也是游泳的。串行垃圾收集器在一定的硬件和操作系統(tǒng)的配置時會缺省使用,也可以顯式地用 -XX:+UseSerialGC 參數(shù)來指定。
  2. 并行垃圾收集器(或吞吐垃圾收集器)并行進(jìn)行小垃圾收集,這會顯著減少垃圾收集的的開銷。它適用于中等或大尺寸數(shù)據(jù)的運(yùn)行在多處理器或多線程硬件上的應(yīng)用。并行垃圾收集器也會在一定的硬件和操作系統(tǒng)配置下被缺省使用,同時,也可以使用 -XX:+UseParallelGC 參數(shù)來指定。
    • 更新:“并行壓縮”是 J2SE 5.0 update 6 以上版本的新特性,并在 Java SE 6 之中得到加強(qiáng),該特性允許主回收也并行收集。如果不使用并行壓縮,主回收仍然會單線程運(yùn)行,這會嚴(yán)重限制系統(tǒng)的可伸縮性。并行壓縮可以使用命令行參數(shù) -XX:+UseParallelOldGC 來打開。
  3. 并發(fā)垃圾收集器并發(fā)地進(jìn)行大部分垃圾收集工作(也就是在應(yīng)用運(yùn)行當(dāng)中進(jìn)行)來盡可能煎炒垃圾收集帶來的應(yīng)用停頓。它是為哪些擁有中到大量數(shù)據(jù)的、對響應(yīng)時間要求高于吞吐量要求的應(yīng)用,因?yàn)樽钚』瘯r延的技術(shù)會讓吞吐能力付出代價。并發(fā)垃圾收集器通過 -XX:+UseConcMarkSweepGC 參數(shù)來啟用。
5.1 選擇垃圾收集器

除非你的應(yīng)用有非常嚴(yán)酷的時延要求,那么就運(yùn)行你的應(yīng)用,并讓系統(tǒng)自己選擇垃圾收集器好了。如果有必要的話,就調(diào)整堆的大小來增進(jìn)性能。如果性能仍然無法達(dá)到你的目標(biāo),那就按照如下設(shè)置來選擇一個垃圾收集器。

  1. 如果應(yīng)用的數(shù)據(jù)很少(大約不超過100MB),那么
    • 使用-XX:+UseSerialGC選擇串行垃圾收集器。
  2. 如果應(yīng)用運(yùn)行在單處理器系統(tǒng)中,并且沒有什么時延要求,那么
    • 讓虛擬機(jī)選擇垃圾收集器,或者
    • 使用-XX:+UseSerialGC選擇串行垃圾收集器。
  3. 如果(a)程序峰值性能是第一位的,并且(b)沒有時延要求,或時延要求是一兩秒或更長,那么
    • 讓虛擬機(jī)選擇垃圾收集器,或者
    • 使用-XX:+UseParallelGC選擇并行垃圾收集器,乃至(可選)通過 -XX:+UseParallelOldGC啟用并行壓縮。
  4. 如果響應(yīng)時間比總體吞吐量更為重要,并且垃圾收集時延需要控制在1秒以內(nèi),那么
    • select the concurrent collector with -XX:+UseConcMarkSweepGC. If only one or two processors are available, consider using incremental mode, described below.
    • 通過 -XX:+UseConcMarkSweepGC 參數(shù)啟用并發(fā)垃圾收集器。進(jìn)當(dāng)你有一個或兩個處理器可用的時候,考慮使用下文將要介紹的“增量模式”。

這些指導(dǎo)意見僅僅是選擇垃圾收集器的起點(diǎn),因?yàn)樾阅芤蕾囉诙训某叽?、?yīng)用中活數(shù)據(jù)的數(shù)量,以及處理器的數(shù)量和速度。時延參數(shù)對這些因素尤為敏感,所以,所謂的1秒門限值只是個大致數(shù)值:在很多硬件和數(shù)據(jù)量的組合情況下,并行垃圾收集器可能會導(dǎo)致停頓時間超過1秒;同樣,在某些組合下,并發(fā)垃圾收集器也不能保證停頓小于1秒。

如果推薦的垃圾收集器沒有達(dá)到期望的性能,首先應(yīng)該嘗試堆和代的尺寸,以期達(dá)到目標(biāo)。如果仍然不成功的話,嘗試更換一個垃圾收集器:使用并發(fā)垃圾收集器來減少停頓時間,使用并行垃圾收集器來增加多處理器系統(tǒng)中的吞吐量。

#p#

6. 并行垃圾收集器

并行垃圾收集器(也被稱為吞吐量收集器)和串行收集器類似,也是一種分代垃圾收集器;其最大的不同在于它使用了多線程來加快垃圾收集的過程。并行垃圾收集器可以通過參數(shù) -XX:+UseParallelGC 指定。缺省的,只有小回收會并行運(yùn)行,主回收仍然單線程運(yùn)行。不過,通過參數(shù)-XX:+UseParallelOldGC啟動并行壓縮可以讓主回收和小回收都并行運(yùn)行,從而進(jìn)一步減少垃圾收集開銷。

在一個有N個處理器的計算機(jī)上,并行垃圾收集器使用N個垃圾收集器線程。不過,這個數(shù)量可以在命令行參數(shù)里指定(參見下文)。在一臺單處理器的計算機(jī)上,由于線程開銷(比如同步),并行垃圾收集器的性能應(yīng)該不如串行垃圾收集器。然而,當(dāng)應(yīng)用程序有中等或大尺寸的堆的時候,它在一個雙處理器的機(jī)器上就會略優(yōu)于串行垃圾收集器,而如果有多于兩個處理器的話,它就能遠(yuǎn)勝于串行垃圾收集器。

垃圾收集器線程數(shù)的多少可以用-XX:ParallelGCThreads=<N>參數(shù)來控制。如果要使用命令行參數(shù)顯式調(diào)整了堆的尺寸,使用并行垃圾收集器的情況下需要的堆的尺寸和使用串行垃圾收集器情況下的堆的尺寸是一階相等的。使用并行垃圾收集器僅僅是讓小回收造成的停頓更短一些。因?yàn)橛卸鄠€垃圾收集器線程參與小回收的過程,有極少的可能性可能會在將年輕代移動到年老代的過程中造成一些碎片。每個垃圾收集線程都有一塊專屬的年老代的空間,用于年輕代向年老代的移動,將年老代的可用空間劃分為“移動緩沖”(promotion buffer)的過程可能會造成一定的碎片效應(yīng)。減少垃圾收集器線程的數(shù)量可以減少碎片、增加年老代的空間。

6.1 代

正如上面提到的,并行垃圾收集器的代的排布方式和串行垃圾收集器略有不同。其分布如下圖所示。

6.2 功效學(xué)

自 J2SE 5.0 以來,并行垃圾收集器成為了server級機(jī)器的缺省垃圾收集器,詳細(xì)資料可以參考“Garbage Collector Ergonomics”。此外,并行垃圾收集器使用一種自動調(diào)整機(jī)制來指定期望的行為而不是指定代的大小和其他底層調(diào)整細(xì)節(jié)。這些行為包括:

  • 最大垃圾收集停頓時間
  • 吞吐量
  • Footprint (也就是堆尺寸)

最大停頓時間的目標(biāo)由參數(shù)-XX:MaxGCPauseMillis=<N>來指定。這個參數(shù)被解釋為指定停頓時間不得大于< N>毫秒;缺省情況下沒有最大停頓時間目標(biāo)。如果指定了一個停頓時間目標(biāo),堆尺寸和其他垃圾回收相關(guān)參數(shù)就會被相應(yīng)調(diào)整,以便保持垃圾回收時間小于指定的值。注意,這些調(diào)整可能會導(dǎo)致總體吞吐量的降低,而且,在某些情況下,要求的停頓時間目標(biāo)可能無法達(dá)到。

吞吐量目標(biāo)測量垃圾回收時間和非垃圾回收時間(也就是應(yīng)用時間)的比例。這個目標(biāo)時間可以用命令行參數(shù)-XX:GCTimeRatio=< N>來指定,這樣,垃圾回收時間和應(yīng)用時間的比例將是1 / (1 + <N>)。例如,-XX:GCTimeRatio=19設(shè)置1/20活5%的時間用于垃圾回收。缺省值是99,目標(biāo)是1%的時間用于垃圾回收。

最大堆footprint使用已經(jīng)存在的 -Xmx<N> 參數(shù)。此外,如果沒有其他的優(yōu)化目標(biāo)的話,垃圾收集器有一個隱式的最小化堆尺寸的目標(biāo)。

6.2.1 目標(biāo)的優(yōu)先級

目標(biāo)的優(yōu)先級順序如下:

  1. 最大停頓時間目標(biāo)
  2. 吞吐量目標(biāo)
  3. 最小堆尺寸目標(biāo)

最大停頓時間目標(biāo)會被首先滿足。僅當(dāng)最大停頓目標(biāo)被滿足的情況下,才會去滿足吞吐量目標(biāo)。類似的,僅當(dāng)前兩個目標(biāo)都會滿足的情況下,才會考慮去滿足footprint目標(biāo)。

6.2.2 時間段尺寸調(diào)整

每次垃圾收集結(jié)束的時候,垃圾收集器都會更新其保存的平均停頓時間之類的統(tǒng)計參量。同時它會檢查各個目標(biāo)是否被滿足了,是否有調(diào)整代尺寸的需要。這之中的意外情況就是顯式的垃圾收集(比如調(diào)用 System.gc())會在統(tǒng)計和調(diào)整判斷中被忽略掉。

增加和縮小一個代的大小是通過增加活縮小一個固定的百分比來達(dá)到的,這樣一個代要分步來達(dá)到需要的尺寸。增加活所見是以不同的比率來進(jìn)行的。缺省情況下,一次增加 20% 活減少 5%。年輕代和年老代增量的比例分別通過命令行參數(shù) -XX:YoungGenerationSizeIncrement=<Y>和 -XX:TenuredGenerationSizeIncrement=<T>來設(shè)定。而縮小比例的要通過 -XX:AdaptiveSizeDecrementScaleFactor=<D>參數(shù)來設(shè)定。如果增量是X%,那么每次減小量就是 (X/D)%。

如果垃圾收集器決定在啟動的時候增加一個代的大小,會有一個額外的百分比的增量。這個附加的增量隨著收集的次數(shù)而減少,不會長期影響。這個額外增量意在提高啟動速度??s小代的尺寸是沒有這個額外的增量。

如果最大停頓時間目標(biāo)沒有達(dá)到,會有且僅有一個代的大小被縮小。如果兩個代都在目標(biāo)之上,停頓時間較大的那個代會首先被縮小。

如果總體吞吐量目標(biāo)沒有達(dá)到,那么兩個代的大小都會增加。每個都按照各自對垃圾回收時間的貢獻(xiàn)比例分別增加。比如,如果年輕代的垃圾回收時間占去了25%的總垃圾回收時間,并且年輕代的全部增量應(yīng)該是20%,那么這時它的增量就是5%。

6.2.3 缺省堆尺寸

如果沒有在命令行中進(jìn)行設(shè)置,初始和最大堆尺寸會通過計算機(jī)內(nèi)存計算而得。如下表所示,對大小占用的內(nèi)存的比例是由參數(shù) DefaultInitialRAMFraction和DefaultMaxRAMFraction來控制的。(表中的 memory 代表計算機(jī)的系統(tǒng)內(nèi)存數(shù)量。)

  1. Formula   
  2. Default   
  3. initial heap size  
  4. memory / DefaultInitialRAMFraction  
  5. memory / 64  
  6. maximum heap size  
  7. MIN(memory / DefaultMaxRAMFraction, 1GB)  
  8. MIN(memory / 4, 1GB) 

注意,缺省的最大堆尺寸不會超過1GB,不論系統(tǒng)中到底有多少內(nèi)存。

6.3 過多的GC時間和OutOfMemory錯誤

當(dāng)有過多的時間花費(fèi)在垃圾收集上的時候,并行垃圾收集器會跑出 OutOfMemoryError 錯誤:如果超過 98% 的時間花費(fèi)在垃圾收集上并且只有 2% 的堆被釋放的話,就會拋出一個 OutOfMemory。這個功能是用來防止堆太小導(dǎo)致程序長時間無法正常工作而設(shè)計的。如果必要,這個功能可以使用命令行參數(shù) -XX:-UseGCOverheadLimit來關(guān)閉。

6.4 測量

并行垃圾收集器的垃圾收集器詳細(xì)輸出和串行垃圾收集器是一樣的。

7. 并發(fā)垃圾收集器

并發(fā)垃圾收集器適用于那些需要更短的垃圾收集停頓,并能為此付出程序運(yùn)行期處理器資源的應(yīng)用。典型情況下,那些擁有較多長期存在的對象(年老代比較大),并且運(yùn)行在擁有兩個活更多處理器的應(yīng)用可能會因此獲益。不過,在任何要求很低停頓時間的應(yīng)用都應(yīng)該考慮這個垃圾收集器;比如,擁有較小年老代的交互程序在但處理器上使用并發(fā)垃圾收集器就可以收到明顯的好處,特別是使用增量模式的時候。并發(fā)垃圾收集器可以通過命令行參數(shù) -XX:+UseConcMarkSweepGC來啟動。

和其他垃圾收集器類似,并發(fā)垃圾收集器也是分代的;所以也有小回收和主回收。并發(fā)垃圾收集器通過使用獨(dú)立的垃圾收集線程于應(yīng)用本身的線程并發(fā)執(zhí)行跟蹤所有可及的對象,以期降低主回收導(dǎo)致的停頓。在每個主回收周其中,并發(fā)垃圾收集器會在垃圾收集的開始讓所有應(yīng)用線程暫停一下,并在回收中段再暫停一次。第二次暫停相對而言會更長一些,在此期間會有多個線程來進(jìn)行收集工作。剩下的收集工作包括大部分的活對象跟蹤和清除不可及的對象的工作都由一個或多個和應(yīng)用并發(fā)的垃圾收集器線程來進(jìn)行。小回收會在進(jìn)行的主回收周其中穿插進(jìn)行,其模式和并行垃圾收集器十分類似(特別需要說明的就是,在小回收期間,應(yīng)用線程是會有停頓的)。

并發(fā)垃圾收集器的基本算法在技術(shù)報告 A Generational Mostly-concurrent Garbage Collector里有介紹。主義,實(shí)際的實(shí)現(xiàn)細(xì)節(jié)在不同版本里手有細(xì)微的變化的,因?yàn)槔占饕苍谝恢边M(jìn)步。

7.1 并發(fā)性的開銷

并發(fā)垃圾收集器的短主回收停頓時間是以處理器資源作為代價的(這些資源如果不用在收集器上肯定就要用在應(yīng)用上了)。最明顯的開銷就是并發(fā)地使用了一個或多個處理器資源。在N處理器系統(tǒng)中,垃圾收集的并發(fā)部分會使用K/N的可用處理器,其中 1<=K<=ceiling{N/4}。(注意,K值的上限將來可能會有變化。)并發(fā)垃圾收集器不僅在并發(fā)階段使用處理器,還引入了其他的開銷。所以,盡管并發(fā)垃圾收集器顯著減少了程序的停頓,但和其他垃圾收集器相比,應(yīng)用的總體吞吐量會受到輕微的影響。

在擁有多個處理器的計算機(jī)上,在并發(fā)垃圾收集器運(yùn)行的時候,應(yīng)用程序仍然能使用到CPU,所以,并發(fā)垃圾收集器并沒有讓程序停頓。這通常意味著更短的停頓,談也意味著更少的應(yīng)用可用的處理器資源,并且讓它運(yùn)行得相對比較慢,特別是當(dāng)應(yīng)用可以完全的利用多個CPU核心的時候更是如此。隨著N的上升,垃圾收集器導(dǎo)致的損失會相對變小,而從并發(fā)垃圾收集的獲益則相對提高。下一節(jié)“并發(fā)模式失敗”會討論這種規(guī)模擴(kuò)張的潛在局限。

因?yàn)樵诓l(fā)階段至少有一個處理器用于了垃圾收集,所以在單處理器(單核)系統(tǒng)中,并發(fā)垃圾收集器一般不會帶來什么好處。不過,并發(fā)垃圾收集有一個分離模式可以在單處理器或雙處理器系統(tǒng)中顯著減少停頓時間;后面的增量模式中將會進(jìn)一步介紹其細(xì)節(jié)。

7.2 并發(fā)模式失敗

并發(fā)垃圾收集器使用一個或多個垃圾收集線程在應(yīng)用線程執(zhí)行的同時運(yùn)行,從而在年老代和永久代變滿之前就完成垃圾收集。如前文所述,在一般的操作中,并發(fā)垃圾收集器的大部分跟蹤與清理工作是在程序運(yùn)行的同時進(jìn)行的,所以,程序線程只有極少的停頓。但是,如果并發(fā)垃圾收集器在年老代變滿的時候仍沒有完成垃圾清除工作,或是年老代中的可用空間無法滿足一次分配操作的需要的時候,應(yīng)用就不得不被暫停下來以等待應(yīng)用線程結(jié)束了。這種無法并發(fā)地完成垃圾收集的情況被稱為“并發(fā)模式失敗”,這就需要對并發(fā)垃圾收集器的參數(shù)進(jìn)行調(diào)整了。

7.3 過多的GC時間和OutOfMemory錯誤

并發(fā)垃圾收集器會在垃圾收集消耗時間過多的時候拋出 OutOfMemoryError 錯誤:如果多于 98% 的時間被花費(fèi)在了垃圾手機(jī)上,并且僅有少于 2% 的堆被回收的話,就會拋出 OutOfMemoryError。這個功能是用來防止堆太小導(dǎo)致程序長時間無法正常工作而設(shè)計的。如果必要,這個功能可以使用命令行參數(shù) -XX:-UseGCOverheadLimit來關(guān)閉。

這個策略和并行垃圾收集器是基本一致的,惟一的區(qū)別就是并發(fā)的垃圾收集時間并未計算在內(nèi)。也就是說,只有哪些程序停頓下來進(jìn)行垃圾收集的時間才被計算在內(nèi)了。這些垃圾收集常常是由于并發(fā)模式失敗或是顯式垃圾收集請求(如調(diào)用 System.gc())導(dǎo)致的。

7.4 浮動垃圾

并發(fā)垃圾收集器與 HotSpot 中的其他垃圾收集器一樣,是一種識別至少所有在堆中可以被訪問到的對象的跟蹤收集器。按照Jones and Lins的說法,是一種增量更新(Incremental Update)垃圾收集器。因?yàn)閼?yīng)用現(xiàn)成和垃圾收集器線程在主回收過程中并發(fā)執(zhí)行,那么那些垃圾收集器跟蹤的對象就可能在垃圾收集完成之后變成垃圾這些無法訪問卻還沒有被回收的對象被稱為浮動垃圾(floating garbage)。浮動垃圾的數(shù)量取決于垃圾收集周期的長度和程序中引用更新的頻率,也被稱為轉(zhuǎn)化率(mutation)。而且,另一個原因是年輕代和年老代的收集是獨(dú)立的,彼此都是對方的根。一個粗略的配置規(guī)則是為年老代的浮動垃圾多預(yù)留出20%的空間來。一個垃圾回收周期中的堆中的浮動垃圾會在下一個垃圾回收周期中被回收。

7.5 時延(停頓)

并發(fā)垃圾收集器在一個并發(fā)回收周期中會兩次暫停應(yīng)用。第一次會從根從根(比如從對象線程棧和寄存器、靜態(tài)對象等的引用)和堆的其他部分(如年輕代)開始標(biāo)記所有直接可達(dá)的活的對象。第一次停頓被稱為“初始標(biāo)記停頓”(initial mark pause)。第二次停頓發(fā)生在并發(fā)跟蹤階段末尾,用來發(fā)現(xiàn)由于在垃圾收集線程跟蹤完一個對象之后又被應(yīng)用線程更新了其引用而沒有被并發(fā)跟蹤到的對象。這次停頓被稱為“重標(biāo)記停頓”(remark pause)。

7.6 并發(fā)階段

可達(dá)對象的并發(fā)跟蹤圖發(fā)生在初始標(biāo)記停頓和重標(biāo)記停頓之間。在并發(fā)跟蹤階段中,一個或多個并發(fā)垃圾收集器線程會使用那些本來可能會被應(yīng)用使用的處理器資源,所以盡管不會停頓,計算密集型應(yīng)用可能會在此階段和其他并發(fā)階段受到相當(dāng)?shù)耐掏铝繐p失。在重標(biāo)記停頓之后,還有一個并發(fā)清理階段,會收集所有標(biāo)記為不可達(dá)的對象。一旦手機(jī)周期結(jié)束了,并發(fā)收集器就會進(jìn)入等待階段,這時就基本不會消耗任何計算資源了,直到下一個主回收周期開始為止。

7.7 開始并發(fā)收集周期

在串行收集器中,每當(dāng)年老代滿了的時候都會引發(fā)一次主回收,所有應(yīng)用現(xiàn)成都會在主回收期間暫停運(yùn)行。并發(fā)垃圾收集器與之不同,它需要在足夠早的時間開始垃圾收集,以便能在年老代變滿之前完成垃圾收集;否則的話就會因?yàn)椴l(fā)模式失敗而導(dǎo)致較長的時延。有很多種條件可以觸發(fā)并發(fā)垃圾收集器啟動。

基于最近的歷史記錄,并發(fā)垃圾收集器維護(hù)了一個年老代變滿的預(yù)期剩余時間和一個垃圾收集周期的預(yù)期時間?;谶@些動態(tài)估計,并發(fā)垃圾收集周期會以讓垃圾收集周期在年老代變滿之前完成為目標(biāo)開始并發(fā)垃圾收集周期。因?yàn)椴l(fā)模式失敗的代價非常慘重,這些估值都流出了安全裕量。

并發(fā)垃圾收集在年老代的已用百分比超出了一個初始占有率值(initiating occupancy)的時候也會啟動。這個初始占有率閾值的缺省值大約是 92%,不過這個值可能在不同版本中略有不同。它也可以通過命令行參數(shù)-XX:CMSInitiatingOccupancyFraction=< N> 來手工設(shè)置,其中N是一個0-100的整數(shù),代表年老代的占用百分比。

7.8 調(diào)度中斷

年輕代和年老代的垃圾收集的停頓發(fā)生彼此間是獨(dú)立的。他們不會重合,但可能會連續(xù)發(fā)生,這樣也就讓一個垃圾收集的停頓連上下一個垃圾收集的停頓了,從外界來看就是一個長停頓了。為了避免這種情況,并發(fā)垃圾收集器會調(diào)度重標(biāo)記停頓的時間,使之發(fā)生在前后兩個年輕代停頓之間。這個調(diào)度目前還不應(yīng)用于初始標(biāo)記停頓,因?yàn)樗ǔ戎貥?biāo)記停頓短很多。

7.9 增量模式

并發(fā)垃圾收集器可以在這樣一種模式下工作:并發(fā)階段以增量的方式進(jìn)行?;貞浺幌?,在并發(fā)階段,垃圾回收線程會使用一個或多個處理器。所謂增量模式是指減少長并發(fā)階段的影響,周期性中斷并發(fā)階段,將處理器資源還給應(yīng)用程序。這種模式又稱為“i-cms”,將垃圾收集器的并發(fā)工作劃分到小塊時間,在年輕代垃圾收集之間進(jìn)行。這個功能對于那些工作在沒那么多處理器的機(jī)器上(1或2個處理器的)需要并發(fā)垃圾收集器的低時延應(yīng)用非常有用。

并發(fā)垃圾收集周期通常包括如下幾步:

  • 停止所有的應(yīng)用線程,標(biāo)記從根開始可達(dá)的對象集,然后繼續(xù)所有的應(yīng)用線程
  • 在應(yīng)用線程運(yùn)行的同時,使用一個或更多的處理器,并發(fā)跟蹤可達(dá)的對象圖
  • 使用一個處理器,并發(fā)跟蹤對象圖中在上一步開始之后的各個改動的部分
  • 停止所有的應(yīng)用線程,重新跟蹤根和對象圖中自從上次檢查開始發(fā)生了變化的部分,然后繼續(xù)運(yùn)行線程
  • 使用一個處理器,并發(fā)地把不可達(dá)對象清理到用于分配空間的 free list 上面去。
  • 使用一個處理器并發(fā)地調(diào)整堆的大小,準(zhǔn)備下一個回收周期所需的數(shù)據(jù)結(jié)構(gòu)

正常情況下,并發(fā)垃圾收集器在并發(fā)跟蹤階段使用一個或多個處理器,不會讓出它們。類似的,在清理階段也會始終獨(dú)占地使用一個處理器。這對于對于一個程序的響應(yīng)時間可能是個不小的影響,特別是系統(tǒng)中只有一兩個CPU的時候。增量模式通過將并發(fā)階段分解為一系列的突發(fā)行為來降低這一影響,這些突發(fā)行為會散布在小回收之間。

i-cms 使用占空比來控制并發(fā)收集器自發(fā)的放棄處理器之前的工作量。占空比是年輕代收集之間的允許并發(fā)垃圾收集器運(yùn)行時間的百分比。i-cms 可以根據(jù)應(yīng)用的行為自動計算占空比(這也是推薦的方法,稱為自動步長(auto pacing)),當(dāng)然,也可以通過命令行指定一個固定的值。

7.9.1 命令行參數(shù)

下面是控制 i-cms的命令行參數(shù)(參考下文的初始設(shè)置建議):

參數(shù)

描述

缺省值

J2SE 5.0 及以前

Java SE 6 及以后

-XX:+CMSIncrementalMode

啟動增量模式。注意,并發(fā)垃圾收集器必須也被選擇(-XX:+UseConcMarkSweepGC) ,否則此參數(shù)無效。

disabled

disabled

-XX:+CMSIncrementalPacing

打開自動步長,這樣,增量模式占空比將根據(jù)JVM統(tǒng)計到的信息自動調(diào)整。

disabled

enabled

-XX:CMSIncrementalDutyCycle=<N>

兩次小回收之間的允許并發(fā)收集器運(yùn)行的時間的百分比(0-100)。如果打開自動步長,那么這個值就是初始值。

50

10

-XX:CMSIncrementalDutyCycleMin=<N>

自動步長打開后,占空比值的下限 (0-100)。

10

0

-XX:CMSIncrementalSafetyFactor=<N>

計算占空比值時使用的一個裕量(0-100)

10

10

-XX:CMSIncrementalOffset=<N>

在小回收之間,增量模式中占空比開始的時間,或說是向右的平移量(0-100)

0

0

-XX:CMSExpAvgFactor=<N>

當(dāng)進(jìn)行并發(fā)回收統(tǒng)計,計算指數(shù)平均值時,當(dāng)前采樣所用的權(quán)值(0-100)

25

25

7.9.2 建議參數(shù)

要在 Java SE 6 里使用 i-cms,需要使用如下命令行參數(shù)

  1. -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode \  
  2. -XX:+PrintGCDetails -XX:+PrintGCTimeStamps 

前兩個參數(shù)分別啟動并發(fā)垃圾收集器和 i-cms。后兩個參數(shù)不是必須的,它們只是要求垃圾收集器將診斷信息打印到標(biāo)準(zhǔn)輸出,這樣,垃圾收集器的行為就可以被看到并用于以后分析了。

注意,對于 J2SE 5.0 和之前的版本,我們建議 i-cms 使用如下的初始命令行參數(shù):

  1. -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode \  
  2. -XX:+PrintGCDetails -XX:+PrintGCTimeStamps \  
  3. -XX:+CMSIncrementalPacing   
  4. -XX:CMSIncrementalDutyCycleMin=0 
  5. -XX:CMSIncrementalDutyCycle=10 

這樣,就是用了和 Java SE 6 一致的參數(shù)了,多出的三個參數(shù)用于自動調(diào)整占空比。這些多余的參數(shù)值完全是使用的 Java SE 6 的缺省值。

#p#

7.9.3 基本問題處理

i-cms 的自動占空比計算模式使用了程序運(yùn)行時收集到的統(tǒng)計信息進(jìn)行占空比計算,以保證并發(fā)垃圾收集器可以在堆占滿之前完成。不過,使用過去的行為預(yù)測將來的變化的估計方式可能并不總是足夠準(zhǔn)確,可能在某些情況下無法阻止堆用滿。如果需要收集的垃圾太多,可以嘗試下面這些步驟,一次使用一個:

Step

Options

1. 增加保險系數(shù)

-XX:CMSIncrementalSafetyFactor=<N>

2. 增加最小占空比

-XX:CMSIncrementalDutyCycleMin=<N>

3. 關(guān)閉自動占空比計算,使用固定占空比

-XX:-CMSIncrementalPacing -XX:CMSIncrementalDutyCycle=<N>

7.10 測量

下面是使用-verbose:gc和-XX:+PrintGCDetails參數(shù)時,并發(fā)垃圾收集器的輸出,一些小細(xì)節(jié)已經(jīng)被去掉了。注意,并發(fā)垃圾收集器的輸出里摻雜著小回收的輸出;典型情況下,很多小回收會發(fā)生在并發(fā)收集周期之中。其中的CMS-initial-mark表征了一個并發(fā)垃圾回收周期的開始。CMS-concurrent-mark: 標(biāo)志著并發(fā)標(biāo)記階段的完成,而CMS-concurrent-sweep則標(biāo)志著并發(fā)清除階段的完成。之前沒有提到過的預(yù)清除階段以CMS- concurrent-preclean為標(biāo)志。預(yù)清除可以和重標(biāo)記階段CMS-remark的準(zhǔn)備工作同時運(yùn)行。最后一個階段是CMS- concurrent-reset,這是下一個并發(fā)收集周期的準(zhǔn)備工作。

  1. [GC [1 CMS-initial-mark: 13991K(20288K)] 14103K(22400K), 0.0023781 secs]
  2. [GC [DefNew: 2112K->64K(2112K), 0.0837052 secs] 16103K->15476K(22400K), 0.0838519 secs]
  3. ...
  4. [GC [DefNew: 2077K->63K(2112K), 0.0126205 secs] 17552K->15855K(22400K), 0.0127482 secs]  
  5. [CMS-concurrent-mark: 0.267/0.374 secs]
  6. [GC [DefNew: 2111K->64K(2112K), 0.0190851 secs] 17903K->16154K(22400K), 0.0191903 secs]  
  7. [CMS-concurrent-preclean: 0.044/0.064 secs]  
  8. [GC [1 CMS-remark: 16090K(20288K)] 17242K(22400K), 0.0210460 secs]  
  9. [GC [DefNew: 2112K->63K(2112K), 0.0716116 secs] 18177K->17382K(22400K), 0.0718204 secs]  
  10. [GC [DefNew: 2111K->63K(2112K), 0.0830392 secs] 19363K->18757K(22400K), 0.0832943 secs]  
  11. ...  
  12. [GC [DefNew: 2111K->0K(2112K), 0.0035190 secs] 17527K->15479K(22400K), 0.0036052 secs]  
  13. [CMS-concurrent-sweep: 0.291/0.662 secs]  
  14. [GC [DefNew: 2048K->0K(2112K), 0.0013347 secs] 17527K->15479K(27912K), 0.0014231 secs]  
  15. [CMS-concurrent-reset: 0.016/0.016 secs]  
  16. [GC [DefNew: 2048K->1K(2112K), 0.0013936 secs] 17527K->15479K(27912K), 0.0014814 secs] 

初始標(biāo)記停頓在典型情況下比小回收的停頓時間還要小。而如上例所示,并發(fā)階段(并發(fā)標(biāo)記、并發(fā)預(yù)清除和并發(fā)清除)通常會比小回收長很多。不過注意,應(yīng)用并沒有在這些并發(fā)階段中停頓下來。重標(biāo)記停頓通常和一個小回收的長度相當(dāng)。重標(biāo)記停頓揮手道應(yīng)用的某些特征(如高對象修改頻率可能會增加這個停頓)和上一次小回收的時間(即,更多的年輕代對象可能會增加這個停頓)的影響。

8. 其他考慮

8.1 永久代尺寸

在大部分應(yīng)用中,永久代對于垃圾回收性能沒有顯著的影響。不過,一些應(yīng)用會動態(tài)的生成與加載很多類;比如,一些 JavaServer Pages(JSP)頁面的實(shí)現(xiàn)。這些應(yīng)用可能需要很大的永久代去存放一些多余的類。如果這樣的話,最大永久代的尺寸可以用命令行參數(shù) -XX:MaxPermSize=<N>來增大。

8.2 Finalization; Weak, Soft and Phantom References

一些應(yīng)用使用 finalization 和 weak, soft, phantom 引用與垃圾收集器交互。這些特征可以 Java 語言層帶來性能影響。一個例子是通過 finalization 來關(guān)閉文件描述符,這會導(dǎo)致一個外部資源依賴于垃圾收集器。以來垃圾收集器來管理內(nèi)存之外的資源是個壞主意。

參考資料章節(jié)中的文章深度討論了一些finalization的常見錯誤和用來避免這些錯誤的技術(shù)。

8.3 顯式垃圾回收

應(yīng)用程序和垃圾回收器的另一個交互途徑是顯式調(diào)用 System.gc() 進(jìn)行完整的垃圾回收。這回強(qiáng)制進(jìn)行一次主回收,即使沒有必要(也就是說一次小回收可能就足夠了),所以應(yīng)該避免這種情況。顯式垃圾回收對性能的影響可以通過使用 -XX:+DisableExplicitGC 進(jìn)行比較來進(jìn)行測量,這樣虛擬機(jī)會無視 System.gc() 的。

最常見的顯式調(diào)用垃圾回收的場景是 RMI 的分布式垃圾回收。使用 RMI 的應(yīng)用會引用到其他虛擬機(jī)中的對象。在這種分布式應(yīng)用的場景下,本地堆中的垃圾可能不能被回收掉,所以 RMI 會周期性強(qiáng)制進(jìn)行完整的垃圾回收。這些回收的頻率可以使用參數(shù)來控制。如

java -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 …

這里指定了垃圾回收每小時運(yùn)行一次,而不是缺省的每分鐘一次。不過,這可能會導(dǎo)致某些對象的清除消耗太長時間。這些參數(shù)可以被設(shè)置到高達(dá)Long.MAX_VALUE來讓顯式垃圾回收的間隔時間無限長,如果沒有合適的DGC上限時間的話。

8.4 Soft References

Soft reference在虛擬機(jī)中比在客戶集中存活的更長一些。其清除頻率可以用命令行參數(shù) -XX:SoftRefLRUPolicyMSPerMB=<N>來控制,這可以指定每兆堆空閑空間的 soft reference 保持存活(一旦它不強(qiáng)可達(dá)了)的毫秒數(shù),這意味著每兆堆中的空閑空間中的 soft reference 會(在最后一個強(qiáng)引用被回收之后)存活1秒鐘。注意,這是一個近似的值,因?yàn)?soft reference 只會在垃圾回收時才會被清除,而垃圾回收并不總在發(fā)生。

8.5 Solaris 8 替換 libthread

Solaris 8 操作系統(tǒng)提供了一個替代的線程庫,libthread, 它將線程直接綁定成了輕量級進(jìn)程(LWP)。有些應(yīng)用能夠從中極大獲益,并潛在的對所有多線程應(yīng)用都或多或少的有好處。下面的命令會為 java 啟用替換的 libthread(BASH 格式)

LD_PRELOAD=/usr/lib/lwp/libthread.so.1
export LD_PRELOAD
java ...

這個方法僅對 Solaris 8 適用,因?yàn)閷?Solaris 9 操作系統(tǒng)來說,這是缺省的,而 Solaris 10 中,這是惟一的線程庫。

9. 相關(guān)資源

  1. HotSpot VM Frequently Asked Questions (FAQ)
  2. GC output examples 介紹了如何解釋不同垃圾收集器的輸出。
  3. How to Handle Java Finalization’s Memory-Retention Issues 介紹了一些容易犯的錯誤和避免他們的方法。
  4. Richard Jones and Rafael Lins, Garbage Collection: Algorithms for Automated Dynamic Memory Management, Wiley and Sons (1996), ISBN 0-471-94148-4

在本網(wǎng)站中,名詞“Java Virtual Machine” 和“JVM” 都代表 Java 平臺虛擬機(jī)。

英文原文:http://java.sun.com/javase/technologies/hotspot/gc/gc_tuning_6.html

本文來自:http://wangxu.me/blog/p/209

 

責(zé)任編輯:林師授 來源: wangxu.me
相關(guān)推薦

2015-07-06 10:14:25

Java垃圾回收實(shí)戰(zhàn)

2014-12-19 11:07:40

Java

2012-01-09 16:53:36

JavaJVM

2020-04-22 21:44:18

Java虛擬機(jī)算法

2023-11-23 09:26:50

Java調(diào)優(yōu)

2021-10-05 20:29:55

JVM垃圾回收器

2010-09-26 11:22:22

JVM垃圾回收JVM

2012-01-10 11:19:35

JavaJVM

2012-01-09 17:06:16

JavaJVM

2010-11-05 09:47:11

OracleJava虛擬機(jī)

2020-05-14 13:39:19

Java 垃圾回收機(jī)制

2021-02-04 10:43:52

開發(fā)技能代碼

2021-09-10 00:34:22

Java 線程啟動

2012-01-10 14:25:36

JavaJVM

2020-09-02 07:03:04

虛擬機(jī)HotSpotJava

2025-01-24 00:00:00

JavaHotSpot虛擬機(jī)

2021-01-04 10:08:07

垃圾回收Java虛擬機(jī)

2023-02-26 11:50:04

Hbase程序Oracle

2009-06-18 13:59:33

Java SE 6垃圾回收器

2025-07-04 07:26:58

HotSpot虛擬機(jī)對象
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號