JVM年轻代和老年代垃圾回收
復(fù)制算法
- 概述
復(fù)制算法將內(nèi)存劃分為兩個區(qū)間,在任意時間點,所有動態(tài)分配的對象都只能分配在其中一個區(qū)間(稱為活動區(qū)間),而另外一個區(qū)間(稱為空閑區(qū)間)則是空閑的。
? 當有效內(nèi)存空間耗盡時,JVM將暫停程序運行,開啟復(fù)制算法GC線程。接下來GC線程會將活動區(qū)間內(nèi)的存活對象,全部復(fù)制到空閑區(qū)間,且嚴格按照內(nèi)存地址依次排列,與此同時,GC線程將更新存活對象的內(nèi)存引用地址指向新的內(nèi)存地址。
- 復(fù)制算法優(yōu)點
- 復(fù)制算法的優(yōu)化
內(nèi)存劃為1個Eden區(qū),2個Survivor區(qū),其中Eden區(qū)占80%內(nèi)存空間,每一塊Survivor區(qū)各占10%內(nèi)存空間,比如說Eden區(qū)有800MB內(nèi)存,每一塊Survivor區(qū)就100MB內(nèi)存。
Survivor區(qū),一塊叫From,一塊叫To,對象存在Eden和From塊。當進行GC時,Eden存活的對象全移到To塊,而From中,存活的對象按年齡值確定去向,當達到一定值的對象會移到年老代中,沒有達到值的復(fù)制到To區(qū),經(jīng)過GC后,Eden和From被清空。之后,From和To交換角色,新的From即為原來的To塊,新的To塊即為原來的From塊,且新的To塊中對象年齡加1。
注:年齡閾值可通過-XX:MaxTenuringThreshold可設(shè)置
MinorGC過程
- 觸發(fā)時機:
Eden區(qū)滿時會觸發(fā),但是Survivor區(qū)域滿了不會引發(fā)GC
- 解釋
代碼在運行的過程中,就會不斷的創(chuàng)建各種各樣的對象,這些對象都會優(yōu)先放到新生代的Eden區(qū)和Survivor1區(qū)。
假如新生代的Eden區(qū)和Survivor1區(qū)都快滿了,此時就會觸發(fā)Minor GC,把存活對象轉(zhuǎn)移到Survivor2區(qū)去。此時就會把Eden區(qū)中的存活對象都一次性轉(zhuǎn)移到一塊空著的Survivor區(qū)。接著Eden區(qū)就會被清空。
然后再次分配新對象到Eden區(qū)里,Eden區(qū)和一塊Survivor區(qū)里是有對象的,其中Survivor區(qū)里放的是上一次Minor GC后存活的對象。如果下次再次Eden區(qū)滿,那么再次觸發(fā)Minor GC,就會把Eden區(qū)和放著上一次Minor GC后存活對象的Survivor區(qū)內(nèi)的存活對象,轉(zhuǎn)移到另外一塊Survivor區(qū)去。
對象進入老年代的情況
根據(jù)對象年齡
對象每次在新生代里躲過一次MinorGC然后被轉(zhuǎn)移到一塊Survivor區(qū)域中,那么它的年齡就會增長一歲,默認的設(shè)置下,當對象的年齡達到15歲的時候,也就是躲過15次GC的時候,他就會轉(zhuǎn)移到老年代里去。這個具體是多少歲進入老年代,可以通過JVM參數(shù)“-XX:MaxTenuringThreshold”來設(shè)置,默認是15歲。
動態(tài)對象年齡判斷
還有另外一個規(guī)則可以讓對象進入老年代,不用等待15次GC過后才可以。
當前Survivor區(qū)域里,一批對象的總大小超過了這塊Survivor區(qū)域的內(nèi)存大小的50%,那么此時大于等于這批對象年齡的對象,就可以直接進入老年代了。
假設(shè)這個圖里的Survivor2區(qū)有三個對象,這些對象的年齡一樣,都是3歲然后倆對象加起來對象超過了50MB,超過了Survivor2區(qū)的100MB內(nèi)存大小的一半了,這個時候,Survivor2區(qū)里的大于等于3歲的對象,就要全部進入老年代里去。這就是所謂的動態(tài)年齡判斷的規(guī)則,這條規(guī)則也會讓一些新生代的對象進入老年代。
另外這里要理清楚一個概念,就是實際這個規(guī)則運行的時候是如下的邏輯:年齡1+年齡2+年齡n的多個年齡對象總和超過了Survivor區(qū)域的50%,此時就會把年齡n以上的對象都放入老年代。
大對象直接進入老年代
有一個JVM參數(shù),就是“-XX:PretenureSizeThreshold”,可以把他的值設(shè)置為字節(jié)數(shù),比如“1048576”字節(jié),就是1MB。意思就是,如果你要創(chuàng)建一個大于這個大小的對象,比如一個超大的數(shù)組,或者是別的啥東西,此時就直接把這個大對象放到老年代里去。不會經(jīng)過新生代。之所以這么做,就是要避免新生代里出現(xiàn)那種大對象,然后屢次躲過GC,然后在兩個Survivor區(qū)域里來回復(fù)制多次之后才能進入老年代,
Minor GC后的對象太多
在Minor GC之后發(fā)現(xiàn)剩余的存活對象太多了,沒辦法放入另外一塊Survivor區(qū),此時就必須得把這些對象直接轉(zhuǎn)移到老年代去
老年代空間分配擔保規(guī)則
- 問題引出
如果新生代里有大量對象存活下來,確實是自己的Survivor區(qū)放不下了,必須轉(zhuǎn)移到老年代去,那么如果老年代里空間也不夠放這些對象怎么處理?
- 解決
首先,在執(zhí)行任何一次Minor GC之前,JVM會先檢查一下老年代可用的可用內(nèi)存空間,是否大于新生代所有對象的總大小。防止最極端的情況下,可能新生代Minor GC過后,所有對象都存活下來了新生代所有對象全部要進入老年代。
如果說發(fā)現(xiàn)老年代的內(nèi)存大小是大于新生代所有對象的,此時就可以對新生代發(fā)起一次Minor GC了,因為即使Minor GC之后所有對象都存活,Survivor區(qū)放不下了,也可以轉(zhuǎn)移到老年代去。
假如執(zhí)行Minor GC之前,發(fā)現(xiàn)老年代的可用內(nèi)存已經(jīng)小于了新生代的全部對象大小了那么這個時候有可能在Minor GC之后新生代的對象全部存活下來,然后全部需要轉(zhuǎn)移到老年代去,但是老年代空間又不夠。所以假如Minor GC之前,就會看一個“-XX:-HandlePromotionFailure”的參數(shù)是否設(shè)置了 ,如果有這個參數(shù),那么就會繼續(xù)嘗試進行下一步判斷。
-
- 查看老年代的內(nèi)存大小,是否大于之前每一次Minor GC后進入老年代的對象的平均大小。
例如:之前每次Minor GC后,平均都有10MB左右的對象會進入老年代,那么此時老年代可用內(nèi)存大于10MB。這就說明,很可能這次Minor GC過后也是差不多10MB左右的對象會進入老年代,此時老年代空間是夠的,就可以進行回收。
-
- 如果上面那個步驟判斷失敗了,或者是“-XX:-HandlePromotionFailure”參數(shù)沒設(shè)置,此時就會直接觸發(fā)一次“Full GC”,就是對老年代進行垃圾回收,盡量騰出來一些內(nèi)存空間,然后再執(zhí)行Minor GC。
-
總結(jié)
在發(fā)生Minor GC之前,虛擬機會檢查老年代最大可用的連續(xù)空間是否大于新生代所有對象的總空間。
老年代垃圾回收算法
標記 - 整理算法
復(fù)制收集算法在對象存活率較高時需要進行較多的復(fù)制操作,效率將會降低。更關(guān)鍵的是,如果不想浪費50%的內(nèi)存空間,就需要提供額外的空間進行分配擔保。由于老年代中對象存活率較高,而且找不到其他內(nèi)存進行分配擔保,所以老年代一般不能直接選用這種收集算法。
根據(jù)老年代的特點,有人對“標記 - 清除”進行改進,提出了“標記 - 整理”算法。“標記 - 整理”算法的標記過程與“標記 - 清除”算法相同,但后續(xù)步驟不是直接對可回收對象進行清理,而是讓所有存活的對象都向一端移動,然后直接清理掉端邊界以外的內(nèi)存。
觸發(fā)時機
調(diào)用System.gc時,系統(tǒng)建議執(zhí)行Full GC,但是不必然執(zhí)行
老年代空間不足
空間分配擔保失敗
JDK 1.7 及以前的永久代(方法區(qū))空間不足
通過Minor GC后進入老年代的平均大小大于老年代的可用內(nèi)存
CMS GC處理浮動垃圾時,如果新生代空間不足,則采用空間分配擔保機制,如果老年代空間不足,則觸發(fā)Full GC
Old GC執(zhí)行的時帶上一次Young GC
- OldGC觸發(fā)條件
發(fā)生Young GC之前進行檢查,如果“老年代可用的連續(xù)內(nèi)存空間” < “新生代歷次Young GC后升入老年代的對象總和的平均大小”,說明本次Young GC后可能升入老年代的對象大小,可能超過了老年代當前可用內(nèi)存空間此時必須先觸發(fā)一次Old GC給老年代騰出更多的空間,然后再執(zhí)行Young GC
執(zhí)行Young GC之后有一批對象需要放入老年代,此時老年代就是沒有足夠的內(nèi)存空間存放這些對象了,此時必須立即觸發(fā)一次Old GC(老年代剩余空間大于歷次年輕代進入老年代的平均大小,但是本次回收后進入老年代的對象遠大于歷次的平均大小)
老年代內(nèi)存使用率超過了92%,也要直接觸發(fā)Old GC,當然這個比例是可以通過參數(shù)調(diào)整的
簡單點說就是,就是老年代空間也不夠了,沒法放入更多對象了,這個時候務(wù)必執(zhí)行Old GC對老年代進行垃圾回收。
- 解釋
如果是條件1引起的Old GC 那么說明老年代空間不足,無法進行Young GC,需要先進行一次Old GC然后再進行Young GC,這樣Old GC就發(fā)生在 Yonug GC之前
如果是條件2引起的,那么就是Young GC后空間不足,進而引發(fā)Old GC
在很多JVM的實現(xiàn)機制里,其實在上述幾種條件達到的時候他觸發(fā)的實際上就是Full GC,這個Full GC會包含Young GC、Old GC和永久代的GC
總結(jié)
以上是生活随笔為你收集整理的JVM年轻代和老年代垃圾回收的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中小卖家电商节恐惧症:你们剁手,我们割肉
- 下一篇: opencv保存视频文件很大