Java虚拟机-内存分配策略
Java技術體系中所提倡的自動內存管理策略最終可以歸結為自動化地解決了兩個問題:給對象分配內存以及回收分配給對象的內存;
幾條普遍的分配規則:
1、對象優先在Eden區分配
年輕代分為三個區:1個Eden區+2個Survivor區。
大部分對象在Eden區中生成(大對象可以直接被創建在年老代),還存活的對象將被復制到一個Survivor區,當這個Survivor區滿時,此區的存活對象將被復制到剩下的一個Survivor區中,當這個Survivor區也滿了的時候,從第一個Survivor復制過來的并且此時還存活的對象,將被復制到年老區。
需要注意的是,Survivor的兩個區是對稱的,沒先后關系,所以同一個區中可能同時存在從Eden區復制過來的對象和從前一個Survivor區復制過來的對象,而復制到年老區的就只有從第一個Survivor區過去的對象。并且Survivor區總有一個是空的。
Minor GC(Young GC):對象被創建時,內存的分配首先發生在年輕代,大部分的對象在創建后很快就不再使用,因此很快變得不可達,于是被年輕代的GC機制清理掉,這個GC機制被稱為Minor GC或Young GC。
注意Minor GC并不代表年輕代內存不足,它事實上只表示在Eden區上的GC。
由于絕大部分對象都是短命的,甚至存活不到Survivor區中,所以Eden與Survivor的比例較大,HotSpot默認為8:1.
在Eden區,HotSpot虛擬機使用了兩種技術來加快內存分配。分別是bump-the-pointer和TLAB(Thread- Local Allocation Buffers),這兩種技術的做法分別是:由于Eden區是連續的,因此bump-the-pointer技術的核心就是跟蹤最后創建的一個對象,在對象創建時,只需要檢查最后一個對象后面是否有足夠的內存即可,從而大大加快內存分配速度;而對于TLAB技術是對于多線程而言的,將Eden區分為若干段,每個線程使用獨立的一段,避免相互影響。TLAB結合bump-the-pointer技術,將保證每個線程都使用Eden區的一段,并快速的分配內存;
2、大對象直接分配在老年代
所謂的大對象是指的需要大量連續內存空間的Java對象。比如:那種很長的字符串以及數組。
經常出現大對象容易導致內存還有不少空間時就提前觸發垃圾收集以獲取足夠的連續空間來“安置”它們;
年老代存放從年輕代存活的對象。一般來說年老代存放的都是生命周期較長的對象。
年老代的空間一般比年輕代大,能存放更多的對象,在年老代上發生的GC次數也比年輕代少。
當年老代內存不足時,將執行Major GC,也叫Full GC;
可能存在年老代對象引用新生代對象的情況,如果需要執行Young GC,則可能需要查詢整個老年代以確定是否可以清理回收,這顯然是低效的。解決的方法是,年老代中維護一個512 byte的塊——”card table“,所有老年代對象引用新生代對象的記錄都記錄在這里。Young GC時,只要查這里即可,不用再去查全部老年代,因此性能大大提高。
一般,老年代用的算法是標記-整理算法,即:標記出仍然存活的對象(存在引用的),將所有存活的對象向一端移動,以保證內存的連續;
在發生Minor GC時,虛擬機會檢查每次晉升進入老年代的大小是否大于老年代的剩余空間大小,如果大于,則直接觸發一次Full GC,否則,就查看是否設置了-XX:+HandlePromotionFailure(允許擔保失敗),如果允許,則只會進行MinorGC,此時可以容忍內存分配失敗;如果不允許,則仍然進行Full GC(這代表著如果設置-XX:+Handle PromotionFailure,則觸發MinorGC就會同時觸發Full GC,哪怕老年代還有很多內存,所以,最好不要這樣做)。
使用-XX:PretenureSizeThreshold參數,令大于這個設置值的對象直接在老年代分配;這樣的目的主要是為了避免在Eden區以及2個Survivor區之間發生大量的內存復制;
-XX:PretenureSizeThreshold參數只對Serial 和ParNew 收集器有效,Parallel Sacvenge收集器不認識此參數。
-XX:PretenureSizeThreshold參數的值不能像-Xms一樣直接寫3M要寫成3145728;
3、長時間存活的對象分配在老年代
虛擬機采用了分代收集的思想來管理內存,那么內存回收時就必須能識別哪些對象在新生代,哪些對象應在老年代,所以,虛擬機給每個對象定義了一個對象年齡計數器;
如果對象在Eden區出生并經歷一次Minor GC后仍然存活,并且能被Survivor區所容納,那么對象年齡計數器值為1;
對象在Survivor區中每熬過一次Minor GC,年齡就會增加1;
默認年齡是15,可以通過設置-XX:MaxTenuringThreshold來設置。
4、動態對象年齡判定
為了能更好的適應不同程序的內存狀況,虛擬機并不是永遠都是要求對象的年齡達到了MaxTenuringThreshold才能晉升老年代;
如果在Survivor空間內相同年齡所有對象大小總和大于 Survivor空間的一半,年齡等于或大于該年齡的對象可以直接進入老年代,而無需達到MaxTenuringThreshold設定的年齡;
5、空間分配擔保
在發生Minor GC之前,虛擬機會先檢查老年代最大可用的連續空間是否大于新生代所有對象的總和。如果條件成立,那么Minor GC可以確保是安全的;
如果不成立,那么虛擬機會查看HandlerPromotionFailure設置值是否允許擔保失敗。
如果允許,會檢查老年代最大可 用的連續空間是否大于歷次晉升到老年代對象的平均大小,如果大于,則嘗試進行一次Minor GC;如果小于,則會進行一次Full GC
轉載于:https://www.cnblogs.com/time-info/p/4514190.html
總結
以上是生活随笔為你收集整理的Java虚拟机-内存分配策略的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ssh key生成
- 下一篇: Java-开源工具类