解析Java內(nèi)存分配和回收策略以及MinorGC、MajorGC、FullGC
對(duì)象內(nèi)存分配與回收策略
對(duì)象的內(nèi)存分配,往大方向講,就是在堆上分配〔但也可能經(jīng)過JIT編譯后被拆散為標(biāo)量類型并間接地棧上分配),對(duì)象主要分配在新生代的Eden區(qū)上,如果啟動(dòng)了本地線程分配緩沖,將按線程優(yōu)先在TLAB上分配。少數(shù)情況下也可能會(huì)直接分配在老年代中。
對(duì)象優(yōu)先分配在Eden區(qū),當(dāng)Eden區(qū)可用空間不夠時(shí)會(huì)進(jìn)行MinorGC
大對(duì)象直接進(jìn)入老年代:大對(duì)象即需要大量連續(xù)內(nèi)存空間的對(duì)象(例如很長的字符串及數(shù)組)。虛擬機(jī)提供了一個(gè)-XX:PretenureSizeThreshoId參數(shù),令大于這個(gè)設(shè)置值的對(duì)象直接在老年代分配,這樣做的目的是避免在Eden區(qū)及兩個(gè)區(qū)之間發(fā)生大量的內(nèi)存復(fù)制。注意PretenureSizeThreshoId參數(shù)只對(duì)Serial和ParNew兩款收集器有效。
長期存活的對(duì)象將進(jìn)入老年代:虛擬機(jī)給每個(gè)對(duì)象定義了一個(gè)對(duì)象年齡(Age)計(jì)數(shù)器(存在于對(duì)象頭中)。如果對(duì)象在Eden出生并經(jīng)過第一次MinorGC后仍然存活,并且能被Survivor容納的話,將被移動(dòng)到Survwor空間中,并且對(duì)象年齡設(shè)為1。對(duì)象在Survivor區(qū)中每“熬過”一次MinorGC,年齡就增加1歲,當(dāng)它的年齡增加到一定程度(默認(rèn)為15歲),就將會(huì)被晉升到老年代中。對(duì)象晉升老年代的年齡閾值,可以通過參數(shù)-XX:MaxTenuringThreshoId設(shè)置。
動(dòng)態(tài)年齡判斷:為了能更好地適應(yīng)不同程序的內(nèi)存狀況,虛擬機(jī)并不是永遠(yuǎn)地要求對(duì)象的年齡必須達(dá)到了MaxTenuringThreshold才能晉升老年代,如果在Survivor空間中相同年齡所有對(duì)象大小的總和大于Survivor空間的一半,年齡大于或等于該年齡的對(duì)象就可以直接進(jìn)人老年代,無須等到MaxTenuringThreshoId中要求的年齡。
空間分配擔(dān)保:在發(fā)生Minor GC之前,虛擬機(jī)會(huì)先檢查Survivor空間是否夠用,如果夠用則直接進(jìn)行Minor GC。否則進(jìn)行檢查老年代最大連續(xù)可用空間是否大于新生代的總和,假如大于,那么這個(gè)時(shí)候發(fā)生Minor GC是安全的。假如不大于,那么需要判斷HandlePromotionFailure設(shè)置是否允許擔(dān)保失敗。假如允許,則繼續(xù)判定老年代最大可用的連續(xù)空間是否大于平均晉升到老年代對(duì)象的平均值,如果大于,這個(gè)時(shí)候可以發(fā)生Minor GC ,如果小于或者設(shè)置HandlePromotionFailure不允許擔(dān)保失敗,則需要做一次Full GC。通常會(huì)把HandlePromotionFailure開關(guān)打開,以減少Full GC。
對(duì)象何時(shí)進(jìn)入新生代、老年代
新分配的對(duì)象一般是直接進(jìn)入新生代的。但是如果出現(xiàn)以下的情況,會(huì)讓對(duì)象進(jìn)入老年代。
- 1.新分配的對(duì)象占用空間大于-XX:PretenureSizeThreshold時(shí)直接分配到老年代
- 2.MinorGC的時(shí)候,Survivor中的內(nèi)存不足了,允許分配擔(dān)保時(shí)會(huì)進(jìn)入老年代。
- 3.MinorGC的時(shí)候,對(duì)象的年齡大于-XX:MaxTenuringThreshold(默認(rèn)為15)時(shí),進(jìn)入老年代。對(duì)象年齡存在于對(duì)象頭中,占4bit。
- 4.當(dāng)進(jìn)行MinorGC的時(shí)候,如果在Survivor空間中相同年齡所有對(duì)象大小的總和大于Survivor空間的一半,年齡大于或等于該年齡的對(duì)象就可以直接進(jìn)人老年代。
三種GC介紹
MinorGC
從年輕代空間(包括 Eden 和 Survivor 區(qū)域)回收內(nèi)存被稱為 Minor GC,也叫Young GC。因?yàn)镴ava對(duì)象大多具備朝生夕死的特征,所以MinorGC非常頻繁,一般回收速度也比較快。一般采用復(fù)制算法。
Minor GC觸發(fā)條件
- Eden區(qū)域滿了
- 新生對(duì)象需要分配到新生代的Eden,當(dāng)Eden區(qū)的內(nèi)存不夠時(shí)需要進(jìn)行MinorGC
Major GC/Full GC:
MajorGC:是清理老年代,Major GC發(fā)生過程常常伴隨一次Minor
FullGC:Full GC可以看做是Major GC+Minor GC共同進(jìn)行的一整個(gè)過程,是清理整個(gè)堆空間(包括年輕代和老年代,這里不包含永久代,因?yàn)橛谰么贘DK7之前包含方法區(qū),是一塊與堆分離的區(qū)域;JDK7將靜態(tài)變量從永久代移到堆中;JDK8則完全取消永久代,方法區(qū)存在元空間MetaSpace中,雖然與堆共享一塊內(nèi)存,邏輯上可以認(rèn)為在堆中,但仍然與堆不相連)。Full GC的速度一般會(huì)比 Minor GC慢10倍以上。一般用的是標(biāo)記整理和標(biāo)記清除算法
Full GC觸發(fā)條件
- 上面Minor GC時(shí)介紹中Survivor空間不足時(shí),判斷是否允許擔(dān)保失敗,如果不允許則進(jìn)行Full GC。如果允許,并且每次晉升到老年代的對(duì)象平均大小>老年代最大可用連續(xù)內(nèi)存空間,也會(huì)進(jìn)行Full GC。
- MinorGC后存活的對(duì)象超過了老年代剩余空間
- 方法區(qū)內(nèi)存不足時(shí)
- System.gc(),可用通過-XX:+ DisableExplicitGC來禁止調(diào)用System.gc
- CMS GC異常,CMS運(yùn)行期間預(yù)留的內(nèi)存無法滿足程序需要,就會(huì)出現(xiàn)一次“Concurrent Mode Failure”失敗,會(huì)觸發(fā)Full GC
圖示GC過程
1:初始階段,對(duì)象分配在Eden區(qū)(大對(duì)象直接進(jìn)入老年代,通過-XX:PretenureSizeThreshold配置),此時(shí)S0和S1是空的(圓圈中的數(shù)字代表對(duì)象的年齡)
2:當(dāng)Eden區(qū)滿了之后,進(jìn)行MinorGC,經(jīng)過掃描與標(biāo)記,不再存活的對(duì)象被清除,存活的對(duì)象進(jìn)入Survivor中的S0并且對(duì)象年齡+1,此時(shí)Eden被清空,S1是空的
3:然后隨著對(duì)象增多又一次MinorGC后,Eden區(qū)和S0區(qū)存活的對(duì)象進(jìn)入S1區(qū)并且對(duì)象年齡+1,Eden和S0區(qū)被清空
4:又一次MinorGC后,和上面步驟類似,Eden區(qū)和S1區(qū)存活的對(duì)象進(jìn)入S0區(qū)并且對(duì)象年齡+1,Eden和S1區(qū)被清空
5:對(duì)象每熬過一次MinorGC其年齡就會(huì)加1,達(dá)到年齡閾值(可通過參數(shù)-XX:MaxTenuringThreshold配置)的年輕代對(duì)象會(huì)晉升到老年代,隨著進(jìn)入老年代的對(duì)象越來越多,當(dāng)老年代內(nèi)存不夠用時(shí)會(huì)發(fā)送MajorGC。
到此這篇關(guān)于解析Java內(nèi)存分配和回收策略以及MinorGC、MajorGC、FullGC的文章就介紹到這了,更多相關(guān)Java 內(nèi)存分配 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實(shí)現(xiàn)雙保險(xiǎn)線程的示例代碼
這篇文章主要介紹了Java實(shí)現(xiàn)雙保險(xiǎn)線程的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12Java?SimpleDateFormat與System類使用示例詳解
這篇文章主要介紹了Java?SimpleDateFormat與System類使用示例,對(duì)于SimpleDateFormat類,是一個(gè)用來區(qū)分區(qū)域設(shè)置的方式進(jìn)行日期的是指,以及對(duì)日期進(jìn)行處理分析的一個(gè)實(shí)現(xiàn)類2022-11-11使用maven方式創(chuàng)建springboot項(xiàng)目的方式
使用Spring Initializr創(chuàng)建spring boot項(xiàng)目,因?yàn)橥饩W(wǎng)問題導(dǎo)致很難成功,所以只能使用maven方式,這里介紹下使用maven方式創(chuàng)建springboot項(xiàng)目的方法,感興趣的朋友一起看看吧2022-09-09SpringBoot+SpringSecurity+JWT實(shí)現(xiàn)系統(tǒng)認(rèn)證與授權(quán)示例
本文主要介紹了SpringBoot+SpringSecurity+JWT實(shí)現(xiàn)系統(tǒng)認(rèn)證與授權(quán)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08解決java 分割字符串成數(shù)組時(shí),小圓點(diǎn)不能直接進(jìn)行分割的問題
這篇文章主要介紹了解決java 分割字符串成數(shù)組時(shí),小圓點(diǎn)不能直接進(jìn)行分割的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-12-12