Java 1.5新特性??工效學??的目標是通過最少的命令行調(diào)整,為JVM提供優(yōu)良的性能。工效學試圖為一個應用程序選擇的垃圾收集器、堆大小與運行時間編譯器。
垃圾收集器的選擇何時會對用戶產(chǎn)生影響呢?對許多應用程序來說,它根本沒有影響。也就是說,在垃圾收集產(chǎn)生的暫停的頻率與持續(xù)時間適度的情況下,應用程序可在其規(guī)范內(nèi)執(zhí)行。如果一個大型應用程序出現(xiàn)擴充,產(chǎn)生大量線程、處理器、套接字和許多內(nèi)存,就會出現(xiàn)例外。
如果一個對象再也不能通過運行程序中的任何指針到達,則視其為垃圾。最直接的垃圾收集運算法則簡單地在每個可到達的對象間迭代。那么,剩下的對象即為垃圾。這一方法所用的時間與活動對象的數(shù)目成比例關系,且禁止用于維護許多活動數(shù)據(jù)的大型應用程序。
從Java 2開始,虛擬機合并了許多應用分代收集組合的各種收集運算法則。盡管簡單的垃圾收集檢查堆中的每一個活動對象,但分代收集利用多數(shù)應用程序的幾個憑經(jīng)驗觀察得到的特性來避免額外工作。這些觀察得到的特性中最為重要的一個就是所謂的早期失效率。許多對象分配以后很快“已經(jīng)死亡”。例如,迭代器對象僅在單獨循環(huán)中存活。為優(yōu)化這種情況,我們對內(nèi)存進行分代管理,或在內(nèi)存池中保留不同年齡的對象。當一代裝滿時,就對這個代進行垃圾收集。對象被分配到更年齡對象代,或新生代中。由于早期失效率,多數(shù)對象在那里死亡。
如果垃圾收集器成為瓶頸,你可能希望自定義代的大小。詳細檢查垃圾收集器的輸出,然后探究單個性能計量單位對垃圾收集器參數(shù)的靈敏度。
初始化時,保留一個的地址空間,在必要時才分配給物理內(nèi)存。為對象內(nèi)存保留的全部地址空間可分為新生代和舊生代。新生代由eden和兩個生存空間組成。對象最初分配到eden中。任何時候,一個生存空間為空,并作為下一個空間的目的地,在eden與另一個生存空間中復制活動對象的集合。對象以這種方式在生存空間中復制,直到它們老化,或復制到舊生代中。與舊生代關系密切的第三個代稱為永生代。這是一個特別的代,因為它保留虛擬機所需要的數(shù)據(jù),來描述在Java語言中沒有等同物的對象。例如,描述類與方法的對象存儲在永生代中。
性能因素
Java應用程序(特別是垃圾收集)有兩個性能計量單位:吞吐量與暫停。吞吐量是指在一段較長時間內(nèi),沒有用于垃圾收集的時間百分比。吞吐量包括用于分配的時間(但用于調(diào)整分配速度的時間一般不包括在內(nèi))。暫停是應用程序因為垃圾收集而出現(xiàn)的停頓時間。
一些用戶還對其他因素較為敏感。例如,占用率(footprint) 是一批工作進程的集合,以頁和緩沖行數(shù)計量,在物理內(nèi)存有限或者有很多進程的系統(tǒng)中,占用率可表示擴展性。
反應性(Promptness)是對象死去的時間和內(nèi)存變?yōu)榭捎脮r的時間差,是分布系統(tǒng),包括遠程方法調(diào)用(RMI)中的重要因素。
通常來說,特定的代大小選擇這些因素之間的平衡作用。例如,一個非常大的新生代的吞吐量可以,但這要以犧牲占用率、反應性和暫停時間為代價。你也可以犧牲吞吐量,應用一個小型的新生代來使新生代暫停時間最短。
如果你希望提高有大量處理器的應用程序的性能,你應該使用吞吐量收集器。你可以用命令行標記-XX:+UseParallelGC來激活吞吐量收集器。你可以用ParallelGCThreads選項-XX:ParallelGCThreads=來控制垃圾收集器線程的數(shù)量。
暫停時間目標用命令行標記-XX:MaxGCPauseMillis=來指定,這是對吞吐量收集器的一個暗示,即它需要毫秒或更短的暫停時間。存在有許多調(diào)整代大小的選項,如-XX:YoungGenerationSizeIncrement=用于新生代;而-XX:TenuredGenerationSizeIncrement=用于舊生代。
如果應用程序受益于較短的垃圾收集器暫停,且能夠在應用程序運行時與垃圾收集器共享處理器資源,我建議使用并行低暫停收集器。如果舊生代占用率超出初始占用率(即當前堆的百分比用于并發(fā)收集啟動之前),并發(fā)收集將啟動。
默認的初始占用率約為68%。你可以用參數(shù)-XX:CMSInitiatingOccupancyFraction=進行設置,這里的是當前舊生代大小百分比。你能夠以并發(fā)階段遞增完成的方式使用并發(fā)收集器。這種模式(這里稱之為”i-cms”)將收集器并發(fā)完成的工作分割成時間小段,安排在新生代收集之間。當需要并發(fā)收集器提供短暫停時間的應用程序在擁有少量處理器的機器上運行時,這一特性很有幫助。
微調(diào)垃圾收集
命令行參數(shù)-verbose:gc打印每個收集階段的信息。如果打開此參數(shù),你會在每個垃圾收集階段看到相似的輸出結(jié)果。例如:
[GC 325407K->83000K(776768K), 0.2300771 secs]
[GC 325816K->83372K(776768K), 0.2454258 secs]
[Full GC 267628K->83769K(776768K), 1.8479984 secs]
有兩個次要收集和一個主要收集(完全GC)。標記-XX:+PrintGCDetails打印收集的其他信息。標記-XX:+PrintGCTimeStamps打印每次收集開始的時間。列表A是兩個標記被設定后的結(jié)果。
另外,主要收集的信息由舊生代描述。在這里,舊生代使用率減少到約10%,所用時間約為0.13秒。
許多參數(shù)影響代的大小。在虛擬機的初始階段,堆的整個空間被保留。你可以用-Xmx選項指定保留空間的大小。如果-Xms參數(shù)的值小于-Xmx參數(shù)的值,表示不是所有的保留空間立即提交給虛擬機。堆的不同部分(永生代、舊生代和新生代)必要時可能會超出虛擬空間的限制。
默認情況下,在每次收集時,虛擬機增大或縮小堆的大小,努力將自由空間與活動對象的比例保持在一個特定的范圍內(nèi)。這個范圍是一個百分數(shù),由參數(shù)-XX:MinHeapFreeRatio=和-XX:MaxHeapFreeRatio=設定,-Xms為下限,-Xmx為上限。除非有暫停問題,否則向虛擬機許可盡可能多的內(nèi)存。默認的大小(64MB)一般都太小。你可以在Sun網(wǎng)站找到其他VM選項的說明。
你還可以為新生代的堆設定一個比例。新生代越大,次要收集就越少發(fā)生。但是,在一個有限大小的堆中,新生代越大就意味著舊生代越小,這會增加主要收集的頻率。選擇要根據(jù)應用程序分配的對象的壽命分配來決定。
新生代的大小由NewRatio控制。例如,設定-XX:NewRatio=3說明新生代與舊生代之比為1:3。如果希望的話,可用參數(shù)SurvivorRatio來調(diào)整生存空間的大小,但它并不如性能重要。
例如,-XX:SurvivorRatio=6說明每個生存空間與eden的比為1:6。除非你發(fā)現(xiàn)過多主要收集或暫停時間問題,向新生代許可大量內(nèi)存。
Java 5.0執(zhí)行了三個不同的垃圾收集器。吞吐量收集器使用新生代收集器的并行版本。如果-XX:+UseParallelGC選項傳遞給命令行,則使用并行收集器;如果-Xincgc或-XX:+UseConcMarkSweepGC選項傳遞給命令行,則使用并發(fā)收集器。
在這種情況下,收集過程中應用程序會出現(xiàn)短期暫停。如果-XX:+UseTrainGC傳遞經(jīng)命令行,則使用遞增式低暫停收集器。將來版本將不支持這一功能,但如果你想了解更多信息,請訪問Sun的關于應用這個收集器的文件資料。(提示:不要同時使用-XX:+UseParallelGC和-XX:+UseConcMarkSweepGC。)
結(jié)論
根據(jù)應用程序的需求,垃圾收集可能成為各種應用程序的瓶頸。通過了解應用程序需求與垃圾收集選項,可以減輕垃圾收集的影響。
垃圾收集器的選擇何時會對用戶產(chǎn)生影響呢?對許多應用程序來說,它根本沒有影響。也就是說,在垃圾收集產(chǎn)生的暫停的頻率與持續(xù)時間適度的情況下,應用程序可在其規(guī)范內(nèi)執(zhí)行。如果一個大型應用程序出現(xiàn)擴充,產(chǎn)生大量線程、處理器、套接字和許多內(nèi)存,就會出現(xiàn)例外。
如果一個對象再也不能通過運行程序中的任何指針到達,則視其為垃圾。最直接的垃圾收集運算法則簡單地在每個可到達的對象間迭代。那么,剩下的對象即為垃圾。這一方法所用的時間與活動對象的數(shù)目成比例關系,且禁止用于維護許多活動數(shù)據(jù)的大型應用程序。
從Java 2開始,虛擬機合并了許多應用分代收集組合的各種收集運算法則。盡管簡單的垃圾收集檢查堆中的每一個活動對象,但分代收集利用多數(shù)應用程序的幾個憑經(jīng)驗觀察得到的特性來避免額外工作。這些觀察得到的特性中最為重要的一個就是所謂的早期失效率。許多對象分配以后很快“已經(jīng)死亡”。例如,迭代器對象僅在單獨循環(huán)中存活。為優(yōu)化這種情況,我們對內(nèi)存進行分代管理,或在內(nèi)存池中保留不同年齡的對象。當一代裝滿時,就對這個代進行垃圾收集。對象被分配到更年齡對象代,或新生代中。由于早期失效率,多數(shù)對象在那里死亡。
如果垃圾收集器成為瓶頸,你可能希望自定義代的大小。詳細檢查垃圾收集器的輸出,然后探究單個性能計量單位對垃圾收集器參數(shù)的靈敏度。
初始化時,保留一個的地址空間,在必要時才分配給物理內(nèi)存。為對象內(nèi)存保留的全部地址空間可分為新生代和舊生代。新生代由eden和兩個生存空間組成。對象最初分配到eden中。任何時候,一個生存空間為空,并作為下一個空間的目的地,在eden與另一個生存空間中復制活動對象的集合。對象以這種方式在生存空間中復制,直到它們老化,或復制到舊生代中。與舊生代關系密切的第三個代稱為永生代。這是一個特別的代,因為它保留虛擬機所需要的數(shù)據(jù),來描述在Java語言中沒有等同物的對象。例如,描述類與方法的對象存儲在永生代中。
性能因素
Java應用程序(特別是垃圾收集)有兩個性能計量單位:吞吐量與暫停。吞吐量是指在一段較長時間內(nèi),沒有用于垃圾收集的時間百分比。吞吐量包括用于分配的時間(但用于調(diào)整分配速度的時間一般不包括在內(nèi))。暫停是應用程序因為垃圾收集而出現(xiàn)的停頓時間。
一些用戶還對其他因素較為敏感。例如,占用率(footprint) 是一批工作進程的集合,以頁和緩沖行數(shù)計量,在物理內(nèi)存有限或者有很多進程的系統(tǒng)中,占用率可表示擴展性。
反應性(Promptness)是對象死去的時間和內(nèi)存變?yōu)榭捎脮r的時間差,是分布系統(tǒng),包括遠程方法調(diào)用(RMI)中的重要因素。
通常來說,特定的代大小選擇這些因素之間的平衡作用。例如,一個非常大的新生代的吞吐量可以,但這要以犧牲占用率、反應性和暫停時間為代價。你也可以犧牲吞吐量,應用一個小型的新生代來使新生代暫停時間最短。
如果你希望提高有大量處理器的應用程序的性能,你應該使用吞吐量收集器。你可以用命令行標記-XX:+UseParallelGC來激活吞吐量收集器。你可以用ParallelGCThreads選項-XX:ParallelGCThreads=來控制垃圾收集器線程的數(shù)量。
暫停時間目標用命令行標記-XX:MaxGCPauseMillis=來指定,這是對吞吐量收集器的一個暗示,即它需要毫秒或更短的暫停時間。存在有許多調(diào)整代大小的選項,如-XX:YoungGenerationSizeIncrement=用于新生代;而-XX:TenuredGenerationSizeIncrement=用于舊生代。
如果應用程序受益于較短的垃圾收集器暫停,且能夠在應用程序運行時與垃圾收集器共享處理器資源,我建議使用并行低暫停收集器。如果舊生代占用率超出初始占用率(即當前堆的百分比用于并發(fā)收集啟動之前),并發(fā)收集將啟動。
默認的初始占用率約為68%。你可以用參數(shù)-XX:CMSInitiatingOccupancyFraction=進行設置,這里的是當前舊生代大小百分比。你能夠以并發(fā)階段遞增完成的方式使用并發(fā)收集器。這種模式(這里稱之為”i-cms”)將收集器并發(fā)完成的工作分割成時間小段,安排在新生代收集之間。當需要并發(fā)收集器提供短暫停時間的應用程序在擁有少量處理器的機器上運行時,這一特性很有幫助。
微調(diào)垃圾收集
命令行參數(shù)-verbose:gc打印每個收集階段的信息。如果打開此參數(shù),你會在每個垃圾收集階段看到相似的輸出結(jié)果。例如:
[GC 325407K->83000K(776768K), 0.2300771 secs]
[GC 325816K->83372K(776768K), 0.2454258 secs]
[Full GC 267628K->83769K(776768K), 1.8479984 secs]
有兩個次要收集和一個主要收集(完全GC)。標記-XX:+PrintGCDetails打印收集的其他信息。標記-XX:+PrintGCTimeStamps打印每次收集開始的時間。列表A是兩個標記被設定后的結(jié)果。
另外,主要收集的信息由舊生代描述。在這里,舊生代使用率減少到約10%,所用時間約為0.13秒。
許多參數(shù)影響代的大小。在虛擬機的初始階段,堆的整個空間被保留。你可以用-Xmx選項指定保留空間的大小。如果-Xms參數(shù)的值小于-Xmx參數(shù)的值,表示不是所有的保留空間立即提交給虛擬機。堆的不同部分(永生代、舊生代和新生代)必要時可能會超出虛擬空間的限制。
默認情況下,在每次收集時,虛擬機增大或縮小堆的大小,努力將自由空間與活動對象的比例保持在一個特定的范圍內(nèi)。這個范圍是一個百分數(shù),由參數(shù)-XX:MinHeapFreeRatio=和-XX:MaxHeapFreeRatio=設定,-Xms為下限,-Xmx為上限。除非有暫停問題,否則向虛擬機許可盡可能多的內(nèi)存。默認的大小(64MB)一般都太小。你可以在Sun網(wǎng)站找到其他VM選項的說明。
你還可以為新生代的堆設定一個比例。新生代越大,次要收集就越少發(fā)生。但是,在一個有限大小的堆中,新生代越大就意味著舊生代越小,這會增加主要收集的頻率。選擇要根據(jù)應用程序分配的對象的壽命分配來決定。
新生代的大小由NewRatio控制。例如,設定-XX:NewRatio=3說明新生代與舊生代之比為1:3。如果希望的話,可用參數(shù)SurvivorRatio來調(diào)整生存空間的大小,但它并不如性能重要。
例如,-XX:SurvivorRatio=6說明每個生存空間與eden的比為1:6。除非你發(fā)現(xiàn)過多主要收集或暫停時間問題,向新生代許可大量內(nèi)存。
Java 5.0執(zhí)行了三個不同的垃圾收集器。吞吐量收集器使用新生代收集器的并行版本。如果-XX:+UseParallelGC選項傳遞給命令行,則使用并行收集器;如果-Xincgc或-XX:+UseConcMarkSweepGC選項傳遞給命令行,則使用并發(fā)收集器。
在這種情況下,收集過程中應用程序會出現(xiàn)短期暫停。如果-XX:+UseTrainGC傳遞經(jīng)命令行,則使用遞增式低暫停收集器。將來版本將不支持這一功能,但如果你想了解更多信息,請訪問Sun的關于應用這個收集器的文件資料。(提示:不要同時使用-XX:+UseParallelGC和-XX:+UseConcMarkSweepGC。)
結(jié)論
根據(jù)應用程序的需求,垃圾收集可能成為各種應用程序的瓶頸。通過了解應用程序需求與垃圾收集選項,可以減輕垃圾收集的影響。