JVM內(nèi)存分配與回收策略
注意: 由于微信的限制,本文中的超鏈接可能不會在文中顯示,建議點(diǎn)擊文末的 閱讀原文 查閱。
JVM內(nèi)存分配與回收策略
對象的內(nèi)存分配,就是在堆上分配(也可能經(jīng)過 JIT 編譯后被拆散為標(biāo)量類型并間接在棧上分配)內(nèi)存,對象主要分配在新生代的 Eden 區(qū)上,少數(shù)情況下可能直接分配在老年代,分配規(guī)則不固定,取決于當(dāng)前使用的垃圾收集器組合以及相關(guān)的參數(shù)配置。
對象優(yōu)先在 Eden 分配
大多數(shù)情況下,對象在新生代 Eden 區(qū)中分配。當(dāng) Eden 區(qū)沒有足夠空間進(jìn)行分配時(shí),虛擬機(jī)將發(fā)起一次 Minor GC。
Minor GC vs Major GC/Full GC:
?Minor GC:回收新生代(包括 Eden 和 Survivor 區(qū)域),因?yàn)?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 都沒有一個(gè)正式的定義,所以有人也簡單地認(rèn)為 Major GC 清理老年代,而 Full GC 清理整個(gè)內(nèi)存堆。
大對象直接進(jìn)入老年代
大對象是指需要大量連續(xù)內(nèi)存空間的 Java 對象,如很長的字符串或數(shù)據(jù)。
一個(gè)大對象能夠存入 Eden 區(qū)的概率比較小,發(fā)生分配擔(dān)保的概率比較大,而分配擔(dān)保需要涉及大量的復(fù)制,就會造成效率低下。
虛擬機(jī)提供了一個(gè) -XX:PretenureSizeThreshold 參數(shù),令大于這個(gè)設(shè)置值的對象直接在老年代分配,這樣做的目的是避免在 Eden 區(qū)及兩個(gè) Survivor 區(qū)之間發(fā)生大量的內(nèi)存復(fù)制。(還記得嗎,新生代采用復(fù)制算法回收垃圾)
長期存活的對象將進(jìn)入老年代
JVM 給每個(gè)對象定義了一個(gè)對象年齡計(jì)數(shù)器。當(dāng)新生代發(fā)生一次 Minor GC 后,存活下來的對象年齡 + 1,當(dāng)年齡超過一定值時(shí),就將超過該值的所有對象轉(zhuǎn)移到老年代中去。
使用 -XX:MaxTenuringThreshold 設(shè)置新生代的最大年齡,只要超過該參數(shù)的新生代對象都會被轉(zhuǎn)移到老年代中去。
動態(tài)對象年齡判斷
如果當(dāng)前新生代的 Survivor 中,相同年齡所有對象大小的總和大于 Survivor 空間的一半。年齡 ≥ 該年齡的對象就可以直接進(jìn)入老年代,無須等到 MaxTenuringThreshold 中要求的年齡。
空間分配擔(dān)保
JDK 6 Update 24 之前的規(guī)則
在發(fā)生 Minor GC 之前,虛擬機(jī)會先檢查老年代最大可用的連續(xù)空間是否大于新生代所有對象總空間。
如果這個(gè)條件成立,Minor GC 可以確保是安全的;如果不成立,則虛擬機(jī)會查看 HandlePromotionFailure 值是否設(shè)置為允許擔(dān)保失敗。
如果是,那么會繼續(xù)檢查老年代最大可用的連續(xù)空間是否大于歷次晉升到老年代對象的平均大小,如果大于,將嘗試進(jìn)行一次 Minor GC,盡管這次 Minor GC 是有風(fēng)險(xiǎn)的;如果小于,或者 HandlePrometionFailue 設(shè)置不允許冒險(xiǎn),那此時(shí)也要改為進(jìn)行一次 Full GC。
JDK 6 Update 24 之后的規(guī)則
只要老年代的連續(xù)空間大于新生代對象總大小或者歷次晉升的平均大小,就會進(jìn)行 Minor GC,否則將進(jìn)行 Full GC。
通過清除老年代中廢棄數(shù)據(jù)擴(kuò)大老年代空閑空間,以便給新生代作擔(dān)保。
這個(gè)過程就是分配擔(dān)保。
總結(jié)一下有哪些情況可能會觸發(fā) JVM 進(jìn)行 Full GC
?System.gc() 方法的調(diào)用:此方法的調(diào)用是建議 JVM 進(jìn)行 Full GC,注意這只是建議而非一定,但在很多情況下它會觸發(fā) Full GC,從而增加 Full GC 的頻率。通常情況下我們只需要讓虛擬機(jī)自己去管理內(nèi)存即可,我們可以通過 -XX:+DisableExplicitGC 來禁止調(diào)用 System.gc()。?老年代空間不足:老年代空間不足會觸發(fā) Full GC 操作,如進(jìn)行該操作后空間依然不足,則會拋出如下錯(cuò)誤: java.lang.OutOfMemoryError:Java heap space?永久代空間不足:JVM規(guī)范中運(yùn)行時(shí)數(shù)據(jù)區(qū)域中的方法區(qū),在 HotSpot 虛擬機(jī)中也稱為永久代(Permanent Generation),存放一些類信息、常量、靜態(tài)變量等數(shù)據(jù),當(dāng)系統(tǒng)要加載的類、反射的類和調(diào)用的方法較多時(shí),永久代可能會被占滿,會觸發(fā) Full GC。如果經(jīng)過 Full GC 仍然回收不了,那么 JVM 會拋出如下錯(cuò)誤信息: java.lang.OutOfMemoryError:PermGen space?CMS GC時(shí)出現(xiàn) promotion failed 和 concurrent mode failure: promotion failed 就是上文所說的擔(dān)保失敗,而 concurrent mode failure 是在執(zhí)行 CMS GC 的過程中同時(shí)有對象要放入老年代,而此時(shí)老年代空間不足造成的。?統(tǒng)計(jì)得到的 Minor GC 晉升到老年代的平均大小大于老年代的剩余空間。
Article From:內(nèi)存分配與回收策略
注意: 由于微信的限制,本文中的超鏈接可能不會在文中顯示,建議點(diǎn)擊文末的 閱讀原文 查閱。
歡迎關(guān)注我的公眾號“須彌零一”,更多技術(shù)文章第一時(shí)間推送。
