Java的內(nèi)存分配與回收策略詳解
Java的內(nèi)存分配與回收策略
對象的內(nèi)存分配,就是在堆上分配(也可能經(jīng)過 JIT 編譯后被拆散為標(biāo)量類型并間接在棧上分配),對象主要分配在新生代的 Eden 區(qū)上,少數(shù)情況下可能直接分配在老年代,分配規(guī)則不固定,取決于當(dāng)前使用的垃圾收集器組合以及相關(guān)的參數(shù)配置。
以下列舉幾條最普遍的內(nèi)存分配規(guī)則。
對象優(yōu)先在 Eden 分配
大多數(shù)情況下,對象在新生代 Eden 區(qū)中分配。當(dāng) Eden 區(qū)沒有足夠空間進(jìn)行分配時,虛擬機將發(fā)起一次 Minor GC。
Minor GC vs Major GC/Full GC:
- Minor GC:回收新生代(包括 Eden 和 Survivor 區(qū)域),因為 Java 對象大多都具備朝生夕滅的特性,所以 Minor GC 非常頻繁,一般回收速度也比較快。
- Major GC / Full GC: 回收老年代,出現(xiàn)了 Major GC,經(jīng)常會伴隨至少一次的 Minor GC,但這并非絕對。Major GC 的速度一般會比 Minor GC 慢 10 倍 以上。
在 JVM 規(guī)范中,Major GC 和 Full GC 都沒有一個正式的定義,所以有人也簡單地認(rèn)為 Major GC 清理老年代,而 Full GC 清理整個內(nèi)存堆。
大對象直接進(jìn)入老年代
大對象是指需要大量連續(xù)內(nèi)存空間的 Java 對象,如很長的字符串或數(shù)據(jù)。
一個大對象能夠存入 Eden 區(qū)的概率比較小,發(fā)生分配擔(dān)保的概率比較大,而分配擔(dān)保需要涉及大量的復(fù)制,就會造成效率低下。
虛擬機提供了一個 -XX:PretenureSizeThreshold
參數(shù),令大于這個設(shè)置值的對象直接在老年代分配,這樣做的目的是避免在 Eden 區(qū)及兩個 Survivor 區(qū)之間發(fā)生大量的內(nèi)存復(fù)制。(還記得嗎,新生代采用復(fù)制算法回收垃圾)
長期存活的對象將進(jìn)入老年代
JVM 給每個對象定義了一個對象年齡計數(shù)器。
當(dāng)新生代發(fā)生一次 Minor GC 后,存活下來的對象年齡 +1,當(dāng)年齡超過一定值時,就將超過該值的所有對象轉(zhuǎn)移到老年代中去。
使用 -XXMaxTenuringThreshold
設(shè)置新生代的最大年齡,只要超過該參數(shù)的新生代對象都會被轉(zhuǎn)移到老年代中去。
動態(tài)對象年齡判定
如果當(dāng)前新生代的 Survivor 中,相同年齡所有對象大小的總和大于 Survivor 空間的一半,年齡 >= 該年齡的對象就可以直接進(jìn)入老年代,無須等到 MaxTenuringThreshold
中要求的年齡。
空間分配擔(dān)保
JDK 6 Update 24 之前的規(guī)則是這樣的:
在發(fā)生 Minor GC 之前,虛擬機會先檢查老年代最大可用的連續(xù)空間是否大于新生代所有對象總空間, 如果這個條件成立,Minor GC 可以確保是安全的; 如果不成立,則虛擬機會查看 HandlePromotionFailure
值是否設(shè)置為允許擔(dān)保失敗, 如果是,那么會繼續(xù)檢查老年代最大可用的連續(xù)空間是否大于歷次晉升到老年代對象的平均大小, 如果大于,將嘗試進(jìn)行一次 Minor GC,盡管這次 Minor GC 是有風(fēng)險的; 如果小于,或者 HandlePromotionFailure
設(shè)置不允許冒險,那此時也要改為進(jìn)行一次 Full GC。
JDK 6 Update 24 之后的規(guī)則變?yōu)椋?/p>
只要老年代的連續(xù)空間大于新生代對象總大小或者歷次晉升的平均大小,就會進(jìn)行 Minor GC,否則將進(jìn)行 Full GC。
通過清除老年代中的廢棄數(shù)據(jù)來擴大老年代空閑空間,以便給新生代作擔(dān)保。
這個過程就是分配擔(dān)保。
可能會觸發(fā) JVM 進(jìn)行 Full GC的情況
- System.gc() 方法的調(diào)用此方法的調(diào)用是建議 JVM 進(jìn)行 Full GC,注意這只是建議而非一定,但在很多情況下它會觸發(fā) Full GC,從而增加 Full GC 的頻率。通常情況下我們只需要讓虛擬機自己去管理內(nèi)存即可,我們可以通過-XX:+ DisableExplicitGC 來禁止調(diào)用System.gc()。
- 老年代空間不足 老年代空間不足會觸發(fā) Full GC 操作,若進(jìn)行該操作后空間依然不足,則會拋出如下錯誤:java.lang.OutOfMemoryError: Java heap space
- 永久代空間不足 JVM 規(guī)范中運行時數(shù)據(jù)區(qū)域中的方法區(qū),在 HotSpot 虛擬機中也稱為永久代(Permanet Generation),存放一些類信息、常量、靜態(tài)變量等數(shù)據(jù),當(dāng)系統(tǒng)要加載的類、反射的類和調(diào)用的方法較多時,永久代可能會被占滿,會觸發(fā) Full GC。如果經(jīng)過 Full GC 仍然回收不了,那么 JVM 會拋出如下錯誤信息:java.lang.OutOfMemoryError: PermGen space
- CMS GC 時出現(xiàn) promotion failed 和 concurrent mode failure promotion failed,就是上文所說的擔(dān)保失敗,而 concurrent mode failure 是在執(zhí)行 CMS GC 的過程中同時有對象要放入老年代,而此時老年代空間不足造成的。
- 統(tǒng)計得到的 Minor GC 晉升到舊生代的平均大小大于老年代的剩余空間。
到此這篇關(guān)于Java的內(nèi)存分配與回收策略詳解的文章就介紹到這了,更多相關(guān)Java內(nèi)存分配與回收 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringCloud負(fù)載均衡實現(xiàn)定向路由詳情
這篇文章主要介紹了SpringCloud負(fù)載均衡實現(xiàn)定向路由詳情,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-08-08Java插入修改刪除數(shù)據(jù)庫數(shù)據(jù)的基本方法
這篇文章主要介紹了Java插入修改刪除數(shù)據(jù)庫數(shù)據(jù)的基本方法,是Java入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下2015-10-10Spring Security OAuth 個性化token的使用
這篇文章主要介紹了Spring Security OAuth 個性化token的使用,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-02-02SpringBoot中的@ConfigurationProperties注解的使用
本文將深入探討@ConfigurationProperties注解的概念、用法、工作原理、配置綁定、類型安全以及如何在實際開發(fā)中應(yīng)用它,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-03-03Java中Runnable與Callable接口的區(qū)別詳解
這篇文章主要為大家詳細(xì)介紹了Java中Runnable與Callable接口的區(qū)別,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)Java有一定的幫助,需要的可以參考一下2023-03-03spring中FactoryBean中的getObject()方法實例解析
這篇文章主要介紹了spring中FactoryBean中的getObject()方法實例解析,分享了相關(guān)代碼示例,小編覺得還是挺不錯的,具有一定借鑒價值,需要的朋友可以參考下2018-02-02springMVC中@RequestParam和@RequestPart的區(qū)別
本文主要介紹了springMVC中@RequestParam和@RequestPart的區(qū)別,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-06-06