Java的內存分配與回收策略詳解
Java的內存分配與回收策略
對象的內存分配,就是在堆上分配(也可能經(jīng)過 JIT 編譯后被拆散為標量類型并間接在棧上分配),對象主要分配在新生代的 Eden 區(qū)上,少數(shù)情況下可能直接分配在老年代,分配規(guī)則不固定,取決于當前使用的垃圾收集器組合以及相關的參數(shù)配置。
以下列舉幾條最普遍的內存分配規(guī)則。
對象優(yōu)先在 Eden 分配
大多數(shù)情況下,對象在新生代 Eden 區(qū)中分配。當 Eden 區(qū)沒有足夠空間進行分配時,虛擬機將發(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 都沒有一個正式的定義,所以有人也簡單地認為 Major GC 清理老年代,而 Full GC 清理整個內存堆。
大對象直接進入老年代
大對象是指需要大量連續(xù)內存空間的 Java 對象,如很長的字符串或數(shù)據(jù)。
一個大對象能夠存入 Eden 區(qū)的概率比較小,發(fā)生分配擔保的概率比較大,而分配擔保需要涉及大量的復制,就會造成效率低下。
虛擬機提供了一個 -XX:PretenureSizeThreshold 參數(shù),令大于這個設置值的對象直接在老年代分配,這樣做的目的是避免在 Eden 區(qū)及兩個 Survivor 區(qū)之間發(fā)生大量的內存復制。(還記得嗎,新生代采用復制算法回收垃圾)
長期存活的對象將進入老年代
JVM 給每個對象定義了一個對象年齡計數(shù)器。
當新生代發(fā)生一次 Minor GC 后,存活下來的對象年齡 +1,當年齡超過一定值時,就將超過該值的所有對象轉移到老年代中去。
使用 -XXMaxTenuringThreshold 設置新生代的最大年齡,只要超過該參數(shù)的新生代對象都會被轉移到老年代中去。
動態(tài)對象年齡判定
如果當前新生代的 Survivor 中,相同年齡所有對象大小的總和大于 Survivor 空間的一半,年齡 >= 該年齡的對象就可以直接進入老年代,無須等到 MaxTenuringThreshold 中要求的年齡。
空間分配擔保
JDK 6 Update 24 之前的規(guī)則是這樣的:
在發(fā)生 Minor GC 之前,虛擬機會先檢查老年代最大可用的連續(xù)空間是否大于新生代所有對象總空間, 如果這個條件成立,Minor GC 可以確保是安全的; 如果不成立,則虛擬機會查看 HandlePromotionFailure 值是否設置為允許擔保失敗, 如果是,那么會繼續(xù)檢查老年代最大可用的連續(xù)空間是否大于歷次晉升到老年代對象的平均大小, 如果大于,將嘗試進行一次 Minor GC,盡管這次 Minor GC 是有風險的; 如果小于,或者 HandlePromotionFailure 設置不允許冒險,那此時也要改為進行一次 Full GC。
JDK 6 Update 24 之后的規(guī)則變?yōu)椋?/p>
只要老年代的連續(xù)空間大于新生代對象總大小或者歷次晉升的平均大小,就會進行 Minor GC,否則將進行 Full GC。
通過清除老年代中的廢棄數(shù)據(jù)來擴大老年代空閑空間,以便給新生代作擔保。
這個過程就是分配擔保。
可能會觸發(fā) JVM 進行 Full GC的情況
- System.gc() 方法的調用此方法的調用是建議 JVM 進行 Full GC,注意這只是建議而非一定,但在很多情況下它會觸發(fā) Full GC,從而增加 Full GC 的頻率。通常情況下我們只需要讓虛擬機自己去管理內存即可,我們可以通過-XX:+ DisableExplicitGC 來禁止調用System.gc()。
- 老年代空間不足 老年代空間不足會觸發(fā) Full GC 操作,若進行該操作后空間依然不足,則會拋出如下錯誤:java.lang.OutOfMemoryError: Java heap space
- 永久代空間不足 JVM 規(guī)范中運行時數(shù)據(jù)區(qū)域中的方法區(qū),在 HotSpot 虛擬機中也稱為永久代(Permanet Generation),存放一些類信息、常量、靜態(tài)變量等數(shù)據(jù),當系統(tǒng)要加載的類、反射的類和調用的方法較多時,永久代可能會被占滿,會觸發(fā) Full GC。如果經(jīng)過 Full GC 仍然回收不了,那么 JVM 會拋出如下錯誤信息:java.lang.OutOfMemoryError: PermGen space
- CMS GC 時出現(xiàn) promotion failed 和 concurrent mode failure promotion failed,就是上文所說的擔保失敗,而 concurrent mode failure 是在執(zhí)行 CMS GC 的過程中同時有對象要放入老年代,而此時老年代空間不足造成的。
- 統(tǒng)計得到的 Minor GC 晉升到舊生代的平均大小大于老年代的剩余空間。
到此這篇關于Java的內存分配與回收策略詳解的文章就介紹到這了,更多相關Java內存分配與回收 內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java實用小技能之快速創(chuàng)建List常用幾種方式
java集合可以說無論是面試、刷題還是工作中都是非常常用的,下面這篇文章主要給大家介紹了關于Java實用小技能之快速創(chuàng)建List常用的幾種方式,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2022-12-12
springboot 配置DRUID數(shù)據(jù)源的方法實例分析
這篇文章主要介紹了springboot 配置DRUID數(shù)據(jù)源的方法,結合實例形式分析了springboot 配置阿里DRUID數(shù)據(jù)源的具體步驟與相關操作技巧,需要的朋友可以參考下2019-12-12
java利用Future實現(xiàn)多線程執(zhí)行與結果聚合實例代碼
這篇文章主要給大家介紹了關于java利用Future實現(xiàn)多線程執(zhí)行與結果聚合的相關資料,Future模式的核心,去除了主函數(shù)的等待時間,并使得原本需要等待的時間段可以用于處理其他業(yè)務邏輯,需要的朋友可以參考下2021-12-12
使用@Value為靜態(tài)變量導入并使用導入的靜態(tài)變量進行初始化方式
這篇文章主要介紹了使用@Value為靜態(tài)變量導入并使用導入的靜態(tài)變量進行初始化方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-02-02
mybatis?對于生成的sql語句?自動加上單引號的情況詳解
這篇文章主要介紹了mybatis?對于生成的sql語句?自動加上單引號的情況詳解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01
springboot+vue實現(xiàn)websocket配置過程解析
這篇文章主要介紹了springboot+vue實現(xiàn)websocket配置過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-04-04

