根據上一章所講還留下了幾個問題
1.萬一垃圾回收過后,存活下來的對象超過了10%的內存空間,在另外一塊Survivor區域中放不下咋辦
2.萬一我們突然分配了一個超級大的對象,大到啥程度?新生代找不到連續內存空間來存放,此時咋整?
3.到底一個存活對象要在新生代里這么來回倒騰多少次之后才會被轉移都老年代去?
先說第3點,老年代默認進入規則
1.躲過15次GC之后進入老年代
比如這行代碼,只有kafka類一直存在,那么靜態變量就一直有引用。所以垃圾回收不會做處理
沒“幸存”一次就會對該對象的年齡+1歲,默認設置下,15歲進入老年代
該參數可以通過設置 “-XX:MaxTenuringThreshold”來設置
2.動態年齡判斷
比如說,在當前存放的survivor區域中,如果一批對象的大小大於該survivor區域的50%,那么大於這批對象年齡的對象直接進入老年代
假設這個圖里的Survivor2區有兩個對象,這倆對象的年齡一樣,都是2歲
然后倆對象加起來對象超過了50MB,超過了Survivor2區的100MB內存大小的一半了,這個時候,Survivor2區里的大於等於2歲的對
象,就要全部進入老年代里去。
另外這里要理清楚一個概念,就是實際這個規則運行的時候是如下的邏輯:年齡1+年齡2+年齡n的多個年齡對象總和超過了Survivor區
域的50%,此時就會把年齡n以上的對象都放入老年代。
3.大對象直接進去老年代
參數 “ -XX:PretenureSizeThreshold”,可以把他的值設置為字節數,比如“1048576”字節,就是1MB。
這個參數的意思是,如果要創建大於等於該值得對象,直接存入老年代,避免大對象在年輕代幾塊內存區中轉移的系統消耗開支
現在說第1點
就是如果在Minor GC之后發現剩余的存活對象太多了,沒辦法放入另外一塊Survivor區怎么辦?如下圖。
比如上面這個圖,假設在發生GC的時候,發現Eden區里超過150MB的存活對象,此時沒辦法放入Survivor區中,此時該怎么辦呢?
這個時候就必須得把這些對象直接轉移到老年代去,如下圖所示。
上面放入老年代又引起了一個新的問題,如果老年代也放不下怎么辦
首先,在執行任何一次Minor GC之前,JVM會先檢查一下老年代可用的可用內存空間,是否大於新生代所有對象的總大小。
為啥檢查這個呢?因為最極端的情況下,可能新生代Minor GC過后,所有對象都存活下來了,那豈不是新生代所有對象全部要進入老
年代?如下圖。
如果說發現老年代的內存大小是大於新生代所有對象的,此時就可以放心大膽的對新生代發起一次Minor GC了,因為即使Minor GC之
后所有對象都存活,Survivor區放不下了,也可以轉移到老年代去。
假如Minor GC之前,發現老年代的可用內存已經小於了新生代的全部對象大小了,就會看一個“-XX:-HandlePromotionFailure”的參數是否設置了
如果設置了該參數,則會判斷當前老年代的內存大小是否大於之前每一次minor GC后進入老年代的對象的平均大小
如果該評斷失敗,或者參數沒有設置會觸發一次 “”FULL GC“,對老年代進行一次垃圾回收然后再進行Minor GC將新生代對象轉入
如果判斷成功
可以冒險嘗試一下Minor GC
此時有幾種可能:
第一種可能,Minor GC過后,剩余的存活對象的大小,是小於Survivor區的大小的,那么此時存活對象進入Survivor
區域即可。(通過)
第二種可能,Minor GC過后,剩余的存活對象的大小,是大於 Survivor區域的大小,但是是小於老年代可用內存大小
的,此時就直接進入老年代即可。(放入老年代)
第三種可能,很不幸,Minor GC過后,剩余的存活對象的大小,大於了Survivor區域的大小,也大於了老年代可用內
存的大小。此時老年代都放不下這些存活對象了,就會發生“Handle Promotion Failure”的情況,這個時候就會觸
發一次“Full GC”。(Full GC)
Full GC就是對老年代進行垃圾回收,同時也一般會對新生代進行垃圾回收。
如果要是Full GC過后,老年代還是沒有足夠的空間存放Minor GC過后的剩余存活對象,那么此時就會導致所謂的
“OOM”內存溢出了