關於gc日志中Desired Survivor的疑問和對象晉升老年代的小結


問題背景

(下面的所有內容都是根據書上的Serial/Serial Old收集器下的情況)

在《深入理解JVM》一書中的——3.6.3長期存活的對象將進入老年代的介紹中,

一個例子的jvm參數中加了這一行

-XX:+printTenuringDestribution

意思是希望每次新生代gc后,可以跟蹤Survivor區中的對象的年齡分布。

 

然后還設置了

-XX:MaxTenuringThreshole=1

這是晉升老年代的年齡閾值。

 

然后在gc日志中,出現了這樣的字眼:

[GC [DefNew Desired Survivor size 524288 bytes, new threshold 1(max 1)

- age     1:    414664 bytes,      414664 total

threshold很顯然就是說,設置的晉升老年代的年齡閾值為1,然后下面的age開頭的那行,很明顯就是在描述Survivor中對象的年齡分布。

百度后知道,age 1后面第一個字節是年齡等於這個1的所有對象的內存占用大小;然后后面那個total的bytes值是指年齡<=這個age的對象總共占用的內存大小。

 

然后就被這個Desired Survivor size給卡住了,這是什么呢?渴望的理想的Survivor大小???

 

 

Desired Survivor size和vm參數-XX:TargetSurvivorRatio

要講這個Desired Survivor size就要知道一個參數:-XX:TargetSurvivorRatio

這個參數的含義是:設定survivor區的目標使用率。默認50,即survivor區對象目標使用率為50%

 

如果只有一個MaxTenuringThreshold,只有大於這個年齡的對象才能晉升老年代的話,肯定不足以應付更加復雜的情況,如果有很多還沒到這個你設置的MaxThreshold的對象呆在Survivor區的話,這樣Survivor區的內存很快就會滿的。所以這個年齡閾值其實是在運行的時候會動態更改的。

 

首先,我們要明確,我們設置的MaxTenuringThreshold是最大的閾值,然后在運行的過程中,虛擬機會動態計算晉升的閾值。

來看看JVM中的關鍵源碼:

uint ageTable::compute_tenuring_threshold(size_t survivor_capacity) {
  //TargetSurvivorRatio默認50,意思是:在回收之后希望survivor區的占用率達到這個比例
  size_t desired_survivor_size = (size_t)((((double) survivor_capacity)*TargetSurvivorRatio)/100);
  size_t total = 0;
  uint age = 1;
  assert(sizes[0] == 0, "no objects with age zero should be recorded");
  while (age < table_size) {//table_size=16
    total += sizes[age];
    //如果加上這個年齡的所有對象的大小之后,占用量>期望的大小,就設置age為新的晉升閾值
    if (total > desired_survivor_size) break;
    age++;
  }

  uint result = age < MaxTenuringThreshold ? age : MaxTenuringThreshold;

這里就可以看到這個Desired survivor size的計算公式了:

desired survivor size = (survivor區容量 * TargetSurvivorRatio)/100(其實就是survivor容量乘以這個targetSurvivorRatio的比值)

這個TargetSurvivorRatio就是上面介紹的那個參數設置的值,默認是50,一般很少會去改。

 

然后這個sizes數組是個age table,存的是個個年齡的所有對象的總大小。

所以,代碼的意思是,如果<=某個age(設n歲)的對象累加起來的內存大小,大於我們的desiredSurvivorSize的話,就要看這個n值,如果這個n小於我們設的MaxThreshold,那么這次gc的閾值就是這個n,否則就是我們設的maxThreshold。

 

所以這也是為什么gc日志中會有new threshold 1(max 1)的字眼吧,max是我們設置的,前面那個是動態計算的吧。

 

 

 

晉升老年代的總結

1.擔保機制

新生代中垃圾收集采用的是復制算法,當Survivor區的內存大小不足以裝下一次Minor Gc中所有的存活對象的時候,就啟動擔保機制,將Survivor不夠放的活對象,直接進入到老年代。

 

2.大對象直接進入老年代

虛擬機提供了個-XX:pretenureSizeThreshold參數,令內存大於這個設置值的對象直接在老年代分配。這個參數只對Serial和ParNew收集器有效,Parallel Scavenge收集器不認識這個參數,一般它也不需要設置,如果遇到必須要設置這個參數的場合,可以考慮ParNew+CMS的收集器組合。

 

3.長期存活的對象進入老年代

就是上文說的,在Minor gc中,把age大於設置的-XX:MaxTenuringThresholed值的對象晉升到老年代。

這個age是這樣計算的,jvm為每個對象定義了一個對象年齡(Age)計數器,如果對象在Eden出生並經過第一次Minor GC后仍然存活,並能夠被Survivor區容納的話,將被移到Survivor區中,並且對象年齡設為1。

對象在Survivor區中每“熬過”一次Minor GC,年齡就加一歲。

 

 

4.動態對象年齡判斷

 這里要說明下一個誤區。

書上是這樣講的:如果在Survivor空間中相同年齡所有對象大小的總和大於Survivor空間的一半,年齡大於或等於該年齡的對象就可以直接進入老年代,無須等到MaxTenuringThreshold中要求的年齡。

 

然而我們上面分析過了:

1. 不是某個年齡的對象總和,而是<=某個年齡的對象總和。

2.也不一定是大於SurVivor空間的一半,只是默認TargetSurvivorRatio設為50才是一半,應該是根據這個參數才對。

 

 

參考文章:

https://blog.csdn.net/foolishandstupid/article/details/77596050——《jvm源碼閱讀筆記[2]:你不知道的晉升閾值TenuringThreshold詳解》

https://blog.csdn.net/zero__007/article/details/52797684——《MaxTenuringThreshold 和 TargetSurvivorRatio參數說明》

https://blog.csdn.net/u014493323/article/details/82921740——《jvm誤區--動態對象年齡判定》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM