本部分,我們將關注堆(heap) 中一個主要區域,新生代(young generation)。首先我們會討論為什么調整新生代的參數會對應用的性能如此重要,接著我們將學習新生代相關的JVM參數。
單純從JVM的功能考慮,并不需要新生代,完全可以針對整個堆進行操作。新生代存在的唯一理由是優化垃圾回收(GC)的性能。更具體說,把堆劃分為新生代和老年代有2個好處:簡化了新對象的分配(只在新生代分配內存),可以更有效的清除不再需要的對象(即死對象)(新生代和老年代使用不同的GC算法)
通過廣泛研究面向對象實現的應用,發現一個共同特點:很多對象的生存時間都很短。同時研究發現,新生對象很少引用生存時間長的對象。結合這2個特點,很明顯 GC 會頻繁訪問新生對象,例如在堆中一個單獨的區域,稱之為新生代。在新生代中,GC可以快速標記回收”死對象”,而不需要掃描整個Heap中的存活一段時間的”老對象”。
SUN/Oracle 的HotSpot JVM 又把新生代進一步劃分為3個區域:一個相對大點的區域,稱為”伊甸園區(Eden)”;兩個相對小點的區域稱為”From 幸存區(survivor)”和”To 幸存區(survivor)”。按照規定,新對象會首先分配在 Eden 中(如果新對象過大,會直接分配在老年代中)。在GC中,Eden 中的對象會被移動到survivor中,直至對象滿足一定的年紀(定義為熬過GC的次數),會被移動到老年代。
基于大多數新生對象都會在GC中被收回的假設。新生代的GC 使用復制算法。在GC前To 幸存區(survivor)保持清空,對象保存在 Eden 和 From 幸存區(survivor)中,GC運行時,Eden中的幸存對象被復制到 To 幸存區(survivor)。針對 From 幸存區(survivor)中的幸存對象,會考慮對象年齡,如果年齡沒達到閥值(tenuring threshold),對象會被復制到To 幸存區(survivor)。如果達到閥值對象被復制到老年代。復制階段完成后,Eden 和From 幸存區中只保存死對象,可以視為清空。如果在復制過程中To 幸存區被填滿了,剩余的對象會被復制到老年代中。最后 From 幸存區和 To幸存區會調換下名字,在下次GC時,To 幸存區會成為From 幸存區。

上圖演示GC過程,黃色表示死對象,綠色表示剩余空間,紅色表示幸存對象
總結一下,對象一般出生在Eden區,年輕代GC過程中,對象在2個幸存區之間移動,如果對象存活到適當的年齡,會被移動到老年代。當對象在老年代死亡時,就需要更高級別的GC,更重量級的GC算法(復制算法不適用于老年代,因為沒有多余的空間用于復制)
現在應該能理解為什么新生代大小非常重要了(譯者,有另外一種說法:新生代大小并不重要,影響GC的因素主要是幸存對象的數量),如果新生代過小,會導致新生對象很快就晉升到老年代中,在老年代中對象很難被回收。如果新生代過大,會發生過多的復制過程。我們需要找到一個合適大小,不幸的是,要想獲得一個合適的大小,只能通過不斷的測試調優。這就需要JVM參數了
-XX:NewSize and -XX:MaxNewSize
就像可以通過參數(-Xms and -Xmx) 指定堆大小一樣,可以通過參數指定新生代大小。設置 XX:MaxNewSize 參數時,應該考慮到新生代只是整個堆的一部分,新生代設置的越大,老年代區域就會減少。一般不允許新生代比老年代還大,因為要考慮GC時最壞情況,所有對象都晉升到老年代。(譯者:會發生OOM錯誤) -XX:MaxNewSize 最大可以設置為-Xmx/2 .
考慮性能,一般會通過參數 -XX:NewSize 設置新生代初始大小。如果知道新生代初始分配的對象大小(經過監控) ,這樣設置會有幫助,可以節省新生代自動擴展的消耗。
-XX:NewRatio
可以設置新生代和老年代的相對大小。這種方式的優點是新生代大小會隨著整個堆大小動態擴展。參數 -XX:NewRatio 設置老年代與新生代的比例。例如 -XX:NewRatio=3 指定老年代/新生代為3/1. 老年代占堆大小的 3/4 ,新生代占 1/4 .
如果針對新生代,同時定義絕對值和相對值,絕對值將起作用。下面例子:
$ java -XX:NewSize=32m -XX:MaxNewSize=512m -XX:NewRatio=3 MyApp
以上設置, JVM 會嘗試為新生代分配四分之一的堆大小,但不會小于32MB或大于521MB
在設置新生代大小問題上,使用絕對值還是相對值,不存在通用準則 。如果了解應用的內存使用情況,設置固定大小的堆和新生代更有利,當然也可以設置相對值。如果對應用的內存使用一無所知,正確的做法是不要設置任何參數,如果應用運行良好。很好,我們不用做任何額外動作.如果遇到性能或OutOfMemoryErrors, 在調優之前,首先需要進行一系列有目的的監控測試,縮小問題的根源。
-XX:SurvivorRatio
參數 -XX:SurvivorRatio 與 -XX:NewRatio 類似,作用于新生代內部區域。-XX:SurvivorRatio 指定伊甸園區(Eden)與幸存區大小比例. 例如, -XX:SurvivorRatio=10 表示伊甸園區(Eden)是 幸存區To 大小的10倍(也是幸存區From的10倍).所以,伊甸園區(Eden)占新生代大小的10/12, 幸存區From和幸存區To 每個占新生代的1/12 .注意,兩個幸存區永遠是一樣大的..
設定幸存區大小有什么作用? 假設幸存區相對伊甸園區(Eden)太小, 相應新生對象的伊甸園區(Eden)永遠很大空間, 我們當然希望,如果這些對象在GC時全部被回收,伊甸園區(Eden)被清空,一切正常.然而,如果有一部分對象在GC中幸存下來, 幸存區只有很少空間容納這些對象.結果大部分幸存對象在一次GC后,就會被轉移到老年代 ,這并不是我們希望的.考慮相反情況, 假設幸存區相對伊甸園區(Eden)太大,當然有足夠的空間,容納GC后的幸存對象. 但是過小的伊甸園區(Eden),意味著空間將越快耗盡,增加新生代GC次數,這是不可接受的。
總之,我們希望最小化短命對象晉升到老年代的數量,同時也希望最小化新生代GC 的次數和持續時間.我們需要找到針對當前應用的折中方案, 尋找適合方案的起點是 了解當前應用中對象的年齡分布情況。
-XX:+PrintTenuringDistribution
參數 -XX:+PrintTenuringDistribution 指定JVM 在每次新生代GC時,輸出幸存區中對象的年齡分布。例如:
Desired survivor size 75497472 bytes, new threshold 15 (max 15)
- age 1: 19321624 bytes, 19321624 total
- age 2: 79376 bytes, 19401000 total
- age 3: 2904256 bytes, 22305256 total
第一行說明幸存區To大小為 75 MB. 也有關于老年代閥值(tenuring threshold)的信息, 老年代閥值,意思是對象從新生代移動到老年代之前,經過幾次GC(即, 對象晉升前的最大年齡). 上例中,老年代閥值為15,最大也是15.
之后行表示,對于小于老年代閥值的每一個對象年齡,本年齡中對象所占字節 (如果當前年齡沒有對象,這一行會忽略). 上例中,一次 GC 后幸存對象大約 19 MB, 兩次GC 后幸存對象大約79 KB , 三次GC 后幸存對象大約 3 MB .每行結尾,顯示直到本年齡全部對象大小.所以,最后一行的 total 表示幸存區To 總共被占用22 MB . 幸存區To 總大小為 75 MB ,當前老年代閥值為15,可以斷定在本次GC中,沒有對象會移動到老年代。現在假設下一次GC 輸出為:
Desired survivor size 75497472 bytes, new threshold 2 (max 15)
- age 1: 68407384 bytes, 68407384 total
- age 2: 12494576 bytes, 80901960 total
- age 3: 79376 bytes, 80981336 total
- age 4: 2904256 bytes, 83885592 total
對比前一次老年代分布。明顯的,年齡2和年齡3 的對象還保持在幸存區中,因為我們看到年齡3和4的對象大小與前一次年齡2和3的相同。同時發現幸存區中,有一部分對象已經被回收,因為本次年齡2的對象大小為 12MB ,而前一次年齡1的對象大小為 19 MB。最后可以看到最近的GC中,有68 MB 新對象,從伊甸園區移動到幸存區。
注意,本次GC 幸存區占用總大小 84 MB -大于75 MB. 結果,JVM 把老年代閥值從15降低到2,在下次GC時,一部分對象會強制離開幸存區,這些對象可能會被回收(如果他們剛好死亡)或移動到老年代。
-XX:InitialTenuringThreshold, -XX:MaxTenuringThreshold and -XX:TargetSurvivorRatio
參數 -XX:+PrintTenuringDistribution 輸出中的部分值可以通過其它參數控制。通過 -XX:InitialTenuringThreshold 和 -XX:MaxTenuringThreshold 可以設定老年代閥值的初始值和最大值。另外,可以通過參數 -XX:TargetSurvivorRatio 設定幸存區的目標使用率.例如 , -XX:MaxTenuringThreshold=10 -XX:TargetSurvivorRatio=90 設定老年代閥值的上限為10,幸存區空間目標使用率為90%。
有多種方式,設置新生代行為,沒有通用準則。我們必須清楚以下2中情況:
1 如果從年齡分布中發現,有很多對象的年齡持續增長,在到達老年代閥值之前。這表示 -XX:MaxTenuringThreshold 設置過大
2 如果 -XX:MaxTenuringThreshold 的值大于1,但是很多對象年齡從未大于1.應該看下幸存區的目標使用率。如果幸存區使用率從未到達,這表示對象都被GC回收,這正是我們想要的。 如果幸存區使用率經常達到,有些年齡超過1的對象被移動到老年代中。這種情況,可以嘗試調整幸存區大小或目標使用率。
-XX:+NeverTenure and -XX:+AlwaysTenure
最后,我們介紹2個頗為少見的參數,對應2種極端的新生代GC情況.設置參數 -XX:+NeverTenure , 對象永遠不會晉升到老年代.當我們確定不需要老年代時,可以這樣設置。這樣設置風險很大,并且會浪費至少一半的堆內存。相反設置參數 -XX:+AlwaysTenure, 表示沒有幸存區,所有對象在第一次GC時,會晉升到老年代。
沒有合理的場景使用這個參數。可以在測試環境中,看下這樣設置會發生什么有趣的事.但是并不推薦使用這些參數.
結論
適當的配置新生代非常重要,有相當多的參數可以設置新生代。然而,單獨調整新生代,而不考慮老年代是不可能優化成功的。當調整堆和GC設置時,我們總是應該同時考慮新生代和老年代。
- java
- 設計模式
- 設計模式總覽
- 設計原則
- 工廠方法模式
- 抽象工廠模式
- 單例模式
- 建造者模式
- 原型模式
- 適配器模式
- 裝飾者模式
- 代理模式
- 外觀模式
- 橋接模式
- 組合模式
- 享元模式
- 策略模式
- 模板方法模式
- 觀察者模式
- 迭代子模式
- 責任鏈模式
- 命令模式
- 備忘錄模式
- 狀態模式
- 訪問者模式
- 中介者模式
- 解釋器模式
- 附錄
- JVM相關
- JVM內存結構
- Java虛擬機的內存組成以及堆內存介紹
- Java堆和棧
- 附錄-數據結構的堆棧和內存分配的堆區棧區的區別
- Java內存之Java 堆
- Java內存之虛擬機和內存區域概述
- Java 內存之方法區和運行時常量池
- Java 內存之直接內存(堆外內存)
- JAVA內存模型
- Java內存模型介紹
- 內存模型如何解決緩存一致性問題
- 深入理解Java內存模型——基礎
- 深入理解Java內存模型——重排序
- 深入理解Java內存模型——順序一致性
- 深入理解Java內存模型——volatile
- 深入理解Java內存模型——鎖
- 深入理解Java內存模型——final
- 深入理解Java內存模型——總結
- 內存可見性
- JAVA對象模型
- JVM內存結構 VS Java內存模型 VS Java對象模型
- Java的對象模型
- Java的對象頭
- HotSpot虛擬機
- HotSpot虛擬機對象探秘
- 深入分析Java的編譯原理
- Java虛擬機的鎖優化技術
- 對象和數組并不是都在堆上分配內存的
- 垃圾回收
- JVM內存管理及垃圾回收
- JVM 垃圾回收器工作原理及使用實例介紹
- JVM內存回收理論與實現(對象存活的判定)
- JVM參數及調優
- CMS GC日志分析
- JVM實用參數(一)JVM類型以及編譯器模式
- JVM實用參數(二)參數分類和即時(JIT)編譯器診斷
- JVM實用參數(三)打印所有XX參數及值
- JVM實用參數(四)內存調優
- JVM實用參數(五)新生代垃圾回收
- JVM實用參數(六) 吞吐量收集器
- JVM實用參數(七)CMS收集器
- JVM實用參數(八)GC日志
- Java性能調優原則
- JVM 優化經驗總結
- 面試題整理
- 面試題1
- java日志規約
- Spring安全
- OAtuth2.0簡介
- Spring Session 簡介(一)
- Spring Session 簡介(二)
- Spring Session 簡介(三)
- Spring Security 簡介(一)
- Spring Security 簡介(二)
- Spring Security 簡介(三)
- Spring Security 簡介(四)
- Spring Security 簡介(五)
- Spring Security Oauth2 (一)
- Spring Security Oauth2 (二)
- Spring Security Oauth2 (三)
- SpringBoot
- Shiro
- Shiro和Spring Security對比
- Shiro簡介
- Session、Cookie和Cache
- Web Socket
- Spring WebFlux