JDK8垃圾回收調優指南--(3)代

原文:Java Platform, Standard Edition HotSpot Virtual Machine Garbage Collection Tuning Guide--Generations

Java SE平臺的一個優點是它使開發人員免受內存分配和垃圾收集的複雜性的影響。然而,當垃圾收集是主要瓶頸時,理解這種隱藏實現的某些方面是有用的。垃圾收集器對應用程序使用對象的方式進行假設,這些假設反映在可調參數中,這些參數可以在不犧牲抽象功能的情況下進行調整,以提高性能。

當一個對象不能通過運行程序中的任何指針訪問時,它就被認爲是垃圾。最直接的垃圾收集算法就是迭代每個可達對象。剩下的任何對象都被認爲是垃圾。這種方法所花費的時間與存活對象的數量成正比,這對於維護大量存活對象的大型應用程序來說是禁止的。

虛擬機包含許多不同的垃圾收集算法,這些算法使用分代收集組合在一起。缺乏經驗的垃圾收集會檢查堆中的每個活動對象,分代收集經驗地利用大多數應用程序的觀察到的幾個特性,以最小化回收未使用(垃圾)對象所需的工作。這些觀察到的特性中,最重要的是“弱代假說”,該假說認爲大多數物體只能存活很短的一段時間。

圖3-1中藍色區域就是對象生命週期的經典分佈。x軸是以分配的字節爲單位度量的對象生存期。y軸上的字節數是具有相應生存期的對象中的總字節數。左邊的尖峯部分表示可分配後不久就將被回收(換句話說,已經“死亡”)的對象。例如,迭代器對象只存活在單個循環期間。

Typical Distribution for Lifetimes of Objects

有些對象壽命更長,所以其分佈向右延伸。例如,一些通常在初始化時分配的對象一直存活到進程退出。在這兩個極端之間是一些處於中間計算階段的對象,在這裏被看作是初始峯值右側的部分區域。一些應用程序具有非常不同的外觀分佈,但令人驚訝的是,大量應用程序具有這種通用的形狀。通過關注大多數對象“早逝”(生命週期短)這一事實(“弱代假說”此時成立),高效地回收成爲可能。

爲了優化這個場景,內存按代進行管理(存儲不同年齡對象的內存池)。垃圾收集發生在每一代中,當這一代被填滿時。絕大多數對象都分配在一個專門用於年輕對象(年輕代)的池中,並且大多數對象都死在這裏。當年輕代填滿時,它會導致一次"minor collection"(只回收年輕代,其他代中的垃圾不會被回收)。"minor collection"可以優化,假設'弱代假設'成立,並且年輕代中的大多數對象都是垃圾,可以回收。這類回收的成本與正在被回收對象的數量成正比;年輕代的死亡對象會被快速地回收。通常,在每次"minor collection"中,年輕代存活下來的部分對象會被提升到老年代;最終,老年代將被填滿,必須(被)回收,觸發"major collection"(整個堆都將被回收)。"major collection"通常持續的時間大於"minor collection",因爲涉及的對象數量要多得多。

如上一節人機工程所述,人機工程學動態地選擇垃圾收集器,以在各種應用程序上提供良好的性能。串行垃圾收集器是爲具有小數據集的應用程序設計的,它的默認參數被選擇爲對大多數小應用程序有效。並行(或吞吐量)垃圾收集器用於具有中到大型數據集的應用程序。人機工程學選擇的堆大小參數,加上自適應大小策略的特性,旨在爲服務器應用程序提供良好的性能。

注意:如果垃圾收集成爲瓶頸,您很可能不得不自定義總堆值的大小以及各個代的大小。檢查詳細的垃圾收集器輸出,然後研究各個性能指標對垃圾收集器參數的敏感性。

圖3-2 展示了默認的(內存)分代分佈情況,除了並行收集器和G1:

Default Arrangement of Generations, Except for Parallel Collector and G1

在初始化時,最大地址空間實際上是有預留的,除非需要,否則不會分配給物理內存。保留給對象內存的完整地址空間將被劃分到年輕代和老年代。

年輕代包括eden和兩個survivor空間。大多數對象最初是分配到eden中的。一個survivor空間在任何時候都是空的,並作爲eden中任何存活對象的目的地;另一個survivor空間是下一次複製回收期間的目的地。對象以這種方式在survivor空間之間複製,直到它們(年齡)足夠老到可以終身保存(複製到老年代)。

Performance Considerations

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

  • 吞吐量,是應用程序時間(除GC以外的時間)與總時間的百分比(總時間 = 應用程序執行時間 + GC執行時間;例如,吞吐量99/100意味着總共100秒的程序執行時間內,應用程序線程運行了99秒, GC線程只運行了1秒)。吞吐量包括內存分配花費的時間(但通常不需要對內存分配速度進行調優)。
  • 暫停,是由於發生垃圾回收而導致的應用程序出現無響應的(這段)時間。

用戶對垃圾回收有不同的要求。例如,有些人認爲吞吐量對於web服務是一個正確的度量標準,因爲垃圾收集期間的暫停也許是可以容忍的,或可以被網絡延遲所掩蓋。然而,在交互式圖形程序中,即使是短暫的停頓也可能對用戶體驗產生負面影響。

有些用戶對其他考慮因素很敏感。Footprint(內存佔用)是一個進程的工作集,以page和cache line來度量。在物理內存有限或進程較多的系統上,FootPrint(內存佔用)可能決定可伸縮性。Promptness(及時性)是從對象死亡到其內存可用時的一段時間,這是分佈式系統(包括遠程方法調用RMI)的一個重要考慮因素。

通常來講,爲特定的代選擇大小是這些考慮因素之間的權衡。例如,一個非常大的年輕代可能會最大化吞吐量,但這是以Footprint(內存佔用空間)、Promptness(及時性)和GC停頓爲代價的。一個小的年輕代可以最小化年輕代的GC停頓時間,但這犧牲了吞吐量。一個代的大小不會影響另一代的回收頻率和暫停時間。

沒有一種合適的方法去選擇一個代的大小。最佳的選擇就是由應用程序使用內存的方式以及用戶需求共同決定。因此,虛擬機對垃圾收集器的選擇並不總是最優的,也可能會被Sizing the Generation中的命令行選項覆蓋。

Measurement

吞吐量和內存佔用空間最好使用特定於應用程序的指標來度量。例如,可以使用客戶機負載生成器測試web服務器的吞吐量,而可以使用'pmap'命令在Solaris操作系統上測量服務器的內存佔用。但是,通過檢查虛擬機本身的診斷輸出,可以很容易地估計由於垃圾回收而導致的暫停。

命令行選項'-verbose:gc'會在每次GC時打印關於堆和垃圾收集的信息。例如,下面是一個大型服務器應用程序的輸出:

[GC 325407K->83000K(776768K), 0.2300771 secs]

[GC 325816K->83372K(776768K), 0.2454258 secs]

[Full GC 267628K->83769K(776768K), 1.8479984 secs]

輸出顯示了兩次'minor collection'和一次'major collection'。箭頭之前和之後的數字(例如,第一行中的325407K->83000K)分別表示垃圾回收前後存活對象的大小。在'minor collection'之後,該大小包含了一些垃圾對象(不再存活),但不能回收。這些對象要麼包含在老年代,要麼被老年代引用。

括號中的數字(例如,第一行中的(776768K))是堆的提交大小:在不向操作系統請求更多內存的情況下,Java對象可用的(總)空間量。注意,這個數字只包含了survivor空間的其中一個。除了在垃圾回收期間,在任何給定的時間內都只使用一個survivor空間來存儲對象。

行上的最後一項(例如,0.2300771秒)表示執行GC所花費的時間,在本例中大約是四分之一秒。

與第三行的'major collection'格式相似;注意:"-verbose:gc"生成的輸出格式可能在將來的版本中發生更改。

命令行選項'-XX:+PrintGCDetails'會打印關於回收的一些額外的信息。下面有一個一個串行垃圾收集器使用'-XX:+PrintGCDetails'輸出信息的示例:

[GC [DefNew: 64575K->959K(64576K), 0.0457646 secs] 196016K->133633K(261184K), 0.0459067 secs]
這表明'major collection'恢復了約98%的年輕代,DefNew: 64575K->959K(64576K),耗時0.0457646秒(約45毫秒)。

整個堆的使用減少到51%左右(196016K->133633K(261184K)),並且在最後0.0459067秒的時間表示,回收(在年輕代的回收之上)還有一些額外的開銷。

注意:由'-XX:+PrintGCDetails'生成的輸出格式可能在將來的版本中更改。

選項'-XX:+PrintGCTimeStamp'在每次回收的開始處添加一個時間戳。這有助於瞭解垃圾回收發生的頻率。

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]

收集在應用程序執行大約111秒後開始。"minor collection"大約在同一時間開始。此外,還顯示了由老年代描述的"major collection"信息。老年代的使用減少到約10% (18154K->2311K(24576K)),並花費0.1290354秒(約130毫秒)。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章