解析Java內(nèi)存分配和回收策略以及MinorGC、MajorGC、FullGC
對象內(nèi)存分配與回收策略
對象的內(nèi)存分配,往大方向講,就是在堆上分配〔但也可能經(jīng)過JIT編譯后被拆散為標量類型并間接地棧上分配),對象主要分配在新生代的Eden區(qū)上,如果啟動了本地線程分配緩沖,將按線程優(yōu)先在TLAB上分配。少數(shù)情況下也可能會直接分配在老年代中。

對象優(yōu)先分配在Eden區(qū),當Eden區(qū)可用空間不夠時會進行MinorGC
大對象直接進入老年代:大對象即需要大量連續(xù)內(nèi)存空間的對象(例如很長的字符串及數(shù)組)。虛擬機提供了一個-XX:PretenureSizeThreshoId參數(shù),令大于這個設置值的對象直接在老年代分配,這樣做的目的是避免在Eden區(qū)及兩個區(qū)之間發(fā)生大量的內(nèi)存復制。注意PretenureSizeThreshoId參數(shù)只對Serial和ParNew兩款收集器有效。
長期存活的對象將進入老年代:虛擬機給每個對象定義了一個對象年齡(Age)計數(shù)器(存在于對象頭中)。如果對象在Eden出生并經(jīng)過第一次MinorGC后仍然存活,并且能被Survivor容納的話,將被移動到Survwor空間中,并且對象年齡設為1。對象在Survivor區(qū)中每“熬過”一次MinorGC,年齡就增加1歲,當它的年齡增加到一定程度(默認為15歲),就將會被晉升到老年代中。對象晉升老年代的年齡閾值,可以通過參數(shù)-XX:MaxTenuringThreshoId設置。
動態(tài)年齡判斷:為了能更好地適應不同程序的內(nèi)存狀況,虛擬機并不是永遠地要求對象的年齡必須達到了MaxTenuringThreshold才能晉升老年代,如果在Survivor空間中相同年齡所有對象大小的總和大于Survivor空間的一半,年齡大于或等于該年齡的對象就可以直接進人老年代,無須等到MaxTenuringThreshoId中要求的年齡。
空間分配擔保:在發(fā)生Minor GC之前,虛擬機會先檢查Survivor空間是否夠用,如果夠用則直接進行Minor GC。否則進行檢查老年代最大連續(xù)可用空間是否大于新生代的總和,假如大于,那么這個時候發(fā)生Minor GC是安全的。假如不大于,那么需要判斷HandlePromotionFailure設置是否允許擔保失敗。假如允許,則繼續(xù)判定老年代最大可用的連續(xù)空間是否大于平均晉升到老年代對象的平均值,如果大于,這個時候可以發(fā)生Minor GC ,如果小于或者設置HandlePromotionFailure不允許擔保失敗,則需要做一次Full GC。通常會把HandlePromotionFailure開關打開,以減少Full GC。
對象何時進入新生代、老年代
新分配的對象一般是直接進入新生代的。但是如果出現(xiàn)以下的情況,會讓對象進入老年代。
- 1.新分配的對象占用空間大于-XX:PretenureSizeThreshold時直接分配到老年代
- 2.MinorGC的時候,Survivor中的內(nèi)存不足了,允許分配擔保時會進入老年代。
- 3.MinorGC的時候,對象的年齡大于-XX:MaxTenuringThreshold(默認為15)時,進入老年代。對象年齡存在于對象頭中,占4bit。
- 4.當進行MinorGC的時候,如果在Survivor空間中相同年齡所有對象大小的總和大于Survivor空間的一半,年齡大于或等于該年齡的對象就可以直接進人老年代。
三種GC介紹
MinorGC
從年輕代空間(包括 Eden 和 Survivor 區(qū)域)回收內(nèi)存被稱為 Minor GC,也叫Young GC。因為Java對象大多具備朝生夕死的特征,所以MinorGC非常頻繁,一般回收速度也比較快。一般采用復制算法。
Minor GC觸發(fā)條件
- Eden區(qū)域滿了
- 新生對象需要分配到新生代的Eden,當Eden區(qū)的內(nèi)存不夠時需要進行MinorGC
Major GC/Full GC:
MajorGC:是清理老年代,Major GC發(fā)生過程常常伴隨一次Minor
FullGC:Full GC可以看做是Major GC+Minor GC共同進行的一整個過程,是清理整個堆空間(包括年輕代和老年代,這里不包含永久代,因為永久代在JDK7之前包含方法區(qū),是一塊與堆分離的區(qū)域;JDK7將靜態(tài)變量從永久代移到堆中;JDK8則完全取消永久代,方法區(qū)存在元空間MetaSpace中,雖然與堆共享一塊內(nèi)存,邏輯上可以認為在堆中,但仍然與堆不相連)。Full GC的速度一般會比 Minor GC慢10倍以上。一般用的是標記整理和標記清除算法
Full GC觸發(fā)條件
- 上面Minor GC時介紹中Survivor空間不足時,判斷是否允許擔保失敗,如果不允許則進行Full GC。如果允許,并且每次晉升到老年代的對象平均大小>老年代最大可用連續(xù)內(nèi)存空間,也會進行Full GC。
- MinorGC后存活的對象超過了老年代剩余空間
- 方法區(qū)內(nèi)存不足時
- System.gc(),可用通過-XX:+ DisableExplicitGC來禁止調(diào)用System.gc
- CMS GC異常,CMS運行期間預留的內(nèi)存無法滿足程序需要,就會出現(xiàn)一次“Concurrent Mode Failure”失敗,會觸發(fā)Full GC
圖示GC過程
1:初始階段,對象分配在Eden區(qū)(大對象直接進入老年代,通過-XX:PretenureSizeThreshold配置),此時S0和S1是空的(圓圈中的數(shù)字代表對象的年齡)

2:當Eden區(qū)滿了之后,進行MinorGC,經(jīng)過掃描與標記,不再存活的對象被清除,存活的對象進入Survivor中的S0并且對象年齡+1,此時Eden被清空,S1是空的

3:然后隨著對象增多又一次MinorGC后,Eden區(qū)和S0區(qū)存活的對象進入S1區(qū)并且對象年齡+1,Eden和S0區(qū)被清空

4:又一次MinorGC后,和上面步驟類似,Eden區(qū)和S1區(qū)存活的對象進入S0區(qū)并且對象年齡+1,Eden和S1區(qū)被清空

5:對象每熬過一次MinorGC其年齡就會加1,達到年齡閾值(可通過參數(shù)-XX:MaxTenuringThreshold配置)的年輕代對象會晉升到老年代,隨著進入老年代的對象越來越多,當老年代內(nèi)存不夠用時會發(fā)送MajorGC。

到此這篇關于解析Java內(nèi)存分配和回收策略以及MinorGC、MajorGC、FullGC的文章就介紹到這了,更多相關Java 內(nèi)存分配 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java?SimpleDateFormat與System類使用示例詳解
這篇文章主要介紹了Java?SimpleDateFormat與System類使用示例,對于SimpleDateFormat類,是一個用來區(qū)分區(qū)域設置的方式進行日期的是指,以及對日期進行處理分析的一個實現(xiàn)類2022-11-11
使用maven方式創(chuàng)建springboot項目的方式
使用Spring Initializr創(chuàng)建spring boot項目,因為外網(wǎng)問題導致很難成功,所以只能使用maven方式,這里介紹下使用maven方式創(chuàng)建springboot項目的方法,感興趣的朋友一起看看吧2022-09-09
SpringBoot+SpringSecurity+JWT實現(xiàn)系統(tǒng)認證與授權(quán)示例
本文主要介紹了SpringBoot+SpringSecurity+JWT實現(xiàn)系統(tǒng)認證與授權(quán)示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-08-08
解決java 分割字符串成數(shù)組時,小圓點不能直接進行分割的問題
這篇文章主要介紹了解決java 分割字符串成數(shù)組時,小圓點不能直接進行分割的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12

