JVM的垃圾回收

一、簡述

GC(Garbage Collection):Java/.NET 中的垃圾收集器。Java 是由 C++ 發(fā)展來的。它擯棄了 C++ 中一些繁瑣容易出錯(cuò)的東西,其中有一條就是這個(gè) GC。

在老式的 C/C++ 程序中,程序員定義了一個(gè)變量,就是在內(nèi)存中開辟了一段相應(yīng)的空間來存值。由于內(nèi)存是有限的,所以當(dāng)程序不再需要使用該變量的時(shí)候,就需要銷毀該對象并釋放其所占用的內(nèi)存資源,好重新利用這段空間。在 C/C++ 中,釋放無用變量內(nèi)存空間的事情需要由程序員來處理,就是說當(dāng)程序員認(rèn)為變量沒用了,就手動(dòng)釋放其占用的內(nèi)存。但是這樣非常繁瑣,如果有所遺漏,就可能造成資源浪費(fèi)甚至內(nèi)存泄露。當(dāng)軟件系統(tǒng)比較復(fù)雜,變量多的時(shí)候程序員往往就忘記釋放內(nèi)存或者在不該釋放的時(shí)候釋放內(nèi)存了。

有了 GC,程序員就不再需要手動(dòng)的去控制內(nèi)存的釋放。當(dāng) JVM 或 .NET CLR 發(fā)覺內(nèi)存資源緊張的時(shí)候,就會(huì)自動(dòng)地去清理無用對象(沒有被引用到的對象)所占用的內(nèi)存空間(這里的說法略顯粗略,事實(shí)上何時(shí)清理內(nèi)存是個(gè)復(fù)雜的策略)。如果需要,可以在程序中顯式地使用System.gc()來強(qiáng)制進(jìn)行一次立即的內(nèi)存清理。Java 提供的 GC 功能可以自動(dòng)監(jiān)測對象是否超過了作用域,從而達(dá)到自動(dòng)回收內(nèi)存的目的,Java 的 GC 會(huì)自動(dòng)進(jìn)行管理,調(diào)用方法:System.gc()或者Runtime.getRuntime().gc()

二、堆內(nèi)存劃分及GC類型

1??堆內(nèi)存根據(jù)對象存活的生命周期被劃分為兩塊,一塊是新生代(Young Generation),另一塊是老年代(Tenured Generation),大概比例是:新生代:老年代 == 1:2。新生代又分為 Eden 和 Survivor,二者空間大小比例默認(rèn)為Eden:Survivor == 8:2。幸存區(qū)又分為S0和S1,二者空間大小是一模一樣的。在堆區(qū)之外還有一個(gè)元空間(MetaqSpace)。JDK8 HotSpot 中移除了永久代(Permanet Generation),使用 MetaSpace 來代替,MetaSpace 是使用本地內(nèi)存來存儲(chǔ)類元數(shù)據(jù)信息。內(nèi)存容量取決于操作系統(tǒng)虛擬內(nèi)存大小,通過參數(shù) -MaxMetaspaceSize 來限制 MetaSpace 的大小。

2??“對象進(jìn)入到老年代”的四種情況

  1. Minor GC/Young GC 時(shí),To Survivor 區(qū)不足以存放存活的對象,對象會(huì)直接進(jìn)入到老年代。
  2. 經(jīng)過多次 Minor GC/Young GC 后,如果存活對象的年齡達(dá)到了設(shè)定閾值,則會(huì)晉升到老年代中。
  3. 動(dòng)態(tài)年齡判定規(guī)則,To Survivor 區(qū)中相同年齡的對象,如果其大小之和占到了 To Survivor 區(qū)一半以上的空間,那么大于此年齡的對象會(huì)直接進(jìn)入老年代,而不需要達(dá)到默認(rèn)的分代年齡。
  4. 大對象:由-XX:PretenureSizeThreshold啟動(dòng)參數(shù)控制,若對象大小大于此值,就會(huì)繞過新生代,直接在老年代中分配。HotSpot 虛擬機(jī)提供該參數(shù),指定大于其值的對象直接在老年代分配的意義在于,避免大對象在 Eden 區(qū)及兩個(gè) Survivor 區(qū)之間來回復(fù)制,產(chǎn)生大量的內(nèi)存復(fù)制操作。

3??GC類型

  1. 部分收集(Partial GC):指收集的目標(biāo)不是整個(gè) Java 堆的垃圾收集。其中又分為:

①新生代收集(Minor GC/Young GC):【一般采用復(fù)制-回收算法】
指目標(biāo)只是新生代的垃圾收集,主要是由于 Eden 不夠分配了。這個(gè)區(qū),大多數(shù) Java 對象都是朝生夕滅,所以 Minor GC 是非常頻繁的,回收速度也比較快。

②老年代收集(Major GC/Old GC):【標(biāo)記-清除算法】
指目標(biāo)只是老年代的垃圾收集。目前只有 CMS 收集器會(huì)有單獨(dú)收集老年代的行為。另外請注意“Major GC”這個(gè)說法現(xiàn)在有點(diǎn)混淆,在不同資料上常有不同所指, 讀者需按上下文區(qū)分到底是指老年代的收集還是整堆收集。

③混合收集(Mixed GC):指目標(biāo)是收集整個(gè)新生代以及部分老年代的垃圾收集。目前只有 G1 收集器會(huì)有這種行為。

  1. 整堆收集(Full GC):收集整個(gè) Java 堆和方法區(qū)的垃圾收集。

因?yàn)?Full GC 要對整個(gè)堆進(jìn)行回收,包括 Young、Tenured 和 MetaqSpace。所以比 Partial GC 要慢,因此應(yīng)該盡可能減少 Full GC 的次數(shù)。在對 JVM 調(diào)優(yōu)的過程中,很大一部分工作就是對于 Full GC 的調(diào)節(jié)。

三、堆內(nèi)存垃圾回收過程

在新生代中,由于對象生存期短,每次垃圾回收時(shí)都有大量的對象需要被回收,這時(shí)采用復(fù)制算法。老年代里的對象存活率較高,每次垃圾回收時(shí)只有少量對象需要被回收,沒有額外的空間進(jìn)行分配擔(dān)保,這時(shí)采用標(biāo)記-整理或者標(biāo)記-清除算法。可以根據(jù)不同代的特點(diǎn)采取最適合的收集算法。

分代收集算法

1??Minor GC/Young GC 的回收過程

  1. 大多數(shù)情況下,對象直接在年輕代中的 Eden 區(qū)進(jìn)行分配,如果 Eden 區(qū)域沒有足夠的空間,那么就會(huì)觸發(fā) Minor GC,處理的區(qū)域只有新生代。因?yàn)榇蟛糠謱ο笤诙虝r(shí)間內(nèi)都可以回收掉,因此 Minor GC/Young GC 后只有極少數(shù)的對象能存活下來,而被移動(dòng)到S0區(qū)(采用的是復(fù)制算法),并且對象的年齡設(shè)為 1。

  2. 當(dāng)觸發(fā)下一次 Minor GC/Young GC 時(shí),會(huì)將 Eden 區(qū)和S0區(qū)的存活對象移動(dòng)到S1區(qū),同時(shí)清空 Eden 區(qū)和S0區(qū)。當(dāng)再次觸發(fā) Minor GC/Young GC 時(shí),這時(shí)候處理的區(qū)域就變成了 Eden 區(qū)和S1區(qū)(即S0和S1進(jìn)行角色交換)。如此反復(fù),對象在 Survivor 區(qū)中每“熬過”一次 Minor GC/Young GC,年齡就增加 1 歲,當(dāng)它的年齡增加到一定程度(默認(rèn)為 15 歲),就將會(huì)被晉升到老年代中。對象晉升老年代的閾值,可以通過參數(shù)-XX:MaxTenuringThreshold 設(shè)置。

2??Full GC 的觸發(fā)時(shí)機(jī)

當(dāng)晉升到老年代的對象大于了老年代的剩余空間時(shí),就會(huì)觸發(fā) Full GC。除此之外,還有以下四種情況也會(huì)觸發(fā):

  1. 老年代的內(nèi)存使用率達(dá)到了一定閾值(可通過參數(shù)調(diào)整),直接觸發(fā) Full GC。

  2. 空間分配擔(dān)保
    在發(fā)生 Minor GC/Young GC 之前,虛擬機(jī)必須先檢查老年代最大可用的連續(xù)空間是否大于新生代所有對象總空間。

①如果大于,那這一次 Minor GC/Young GC 可以確保是安全的。
②如果小于,則虛擬機(jī)會(huì)先查看- XX:HandlePromotionFailure的設(shè)置值是否允許擔(dān)保失敗(Handle Promotion Failure):

  • 如果允許,那會(huì)繼續(xù)檢查老年代最大可用的連續(xù)空間是否大于歷次晉升到老年代對象的平均大小,如果大于,將嘗試進(jìn)行一次 Minor GC/Young GC,盡管這次 Minor GC/Young GC 是有風(fēng)險(xiǎn)的;
  • 如果小于,或者-XX: HandlePromotionFailure設(shè)置不允許冒險(xiǎn),那這時(shí)就要改為進(jìn)行一次 Full GC。
  1. Metaspace(元空間)達(dá)到-XX:MetaspaceSize【元空間初始值,以字節(jié)為單位】的指定值時(shí),也會(huì)觸發(fā) Full GC。同時(shí)收集器會(huì)對該值進(jìn)行調(diào)整:如果釋放了大量的空間,就適當(dāng)降低該值;如果釋放 了很少的空間,那么在不超過-XX:MaxMetaspaceSize【元空間最大值,默認(rèn)是-1,即不限制,或者說只受限于本地內(nèi)存大小。】(如果設(shè)置了的話)的情況下,適當(dāng)提高該值。

  2. System.gc()或者Runtime.gc()被顯式調(diào)用時(shí),觸發(fā) Full GC。

四、GC只回收堆內(nèi)存和方法區(qū)內(nèi)的對象

JVM 內(nèi)存區(qū)域中程序計(jì)數(shù)器、虛擬機(jī)棧、本地方法棧三個(gè)區(qū)域隨線程而生、隨線程而滅。因此這三個(gè)區(qū)域的內(nèi)存分配和回收都具備確定性,就不需要過多考慮回收的問題,因?yàn)榉椒ńY(jié)束或者線程結(jié)束時(shí),內(nèi)存自然就跟隨著回收了。而 Java 堆區(qū)和方法區(qū)則不一樣,這部分內(nèi)存的分配和回收是動(dòng)態(tài)的,正是垃圾收集器所需關(guān)注的部分。

垃圾收集器在對堆區(qū)和方法區(qū)進(jìn)行回收前,首先要確定這些區(qū)域的對象哪些可以被回收,哪些暫時(shí)還不能回收,這就要用到判斷對象是否存活的算法:
①引用計(jì)數(shù)算法
②可達(dá)性分析算法
③對象死亡(被回收)前的最后一次掙扎
④方法區(qū)如何判斷是否需要回收

1??引用計(jì)數(shù)算法

引用計(jì)數(shù)是垃圾收集器中的早期策略。在這種方法中,堆中每個(gè)對象實(shí)例都有一個(gè)引用計(jì)數(shù)。當(dāng)一個(gè)對象被創(chuàng)建時(shí),就將該對象實(shí)例分配給一個(gè)變量,該變量計(jì)數(shù)設(shè)置為 1。當(dāng)任何其它變量被賦值為這個(gè)對象的引用時(shí),計(jì)數(shù) +1(a = b,則 b 引用的對象實(shí)例的計(jì)數(shù)器 +1),但當(dāng)一個(gè)對象實(shí)例的某個(gè)引用超過了生命周期或者被設(shè)置為一個(gè)新值時(shí),對象實(shí)例的引用計(jì)數(shù)器 -1。任何引用計(jì)數(shù)器為 0 的對象實(shí)例可以被當(dāng)作垃圾收集。當(dāng)一個(gè)對象實(shí)例被垃圾收集時(shí),它引用的任何對象實(shí)例的引用計(jì)數(shù)器 -1。

優(yōu)點(diǎn):引用計(jì)數(shù)收集器可以很快的執(zhí)行,交織在程序運(yùn)行中。對程序需要不被長時(shí)間打斷的實(shí)時(shí)環(huán)境比較有利。
缺點(diǎn):無法檢測出循環(huán)引用。如父對象有一個(gè)對子對象的引用,子對象反過來引用父對象。這樣,它們的引用計(jì)數(shù)永遠(yuǎn)不可能為 0。

public class ReferenceFindTest {
  public static void main(String[] args) {
     MyObject object1 = new MyObject();
     MyObject object2 = new MyObject();

     object1.object = object2;
     object2.object = object1;

     object1 = null;
     object2 = null;
  }
}

這段代碼是用來驗(yàn)證引用計(jì)數(shù)算法不能檢測出循環(huán)引用。最后兩句將 object1 和 object2 賦值為 null,也就是說 object1 和 object2 指向的對象已經(jīng)不可能再被訪問,但是由于它們互相引用對方,導(dǎo)致它們的引用計(jì)數(shù)器都不為 0,那么垃圾收集器就永遠(yuǎn)不會(huì)回收它們。

2??可達(dá)性分析算法

該算法是從離散數(shù)學(xué)中的圖論引入的,程序把所有的引用關(guān)系看作一張圖,從一個(gè)節(jié)點(diǎn) GC ROOT 開始,尋找對應(yīng)的引用節(jié)點(diǎn),找到這個(gè)節(jié)點(diǎn)以后,繼續(xù)尋找這個(gè)節(jié)點(diǎn)的引用節(jié)點(diǎn),當(dāng)所有的引用節(jié)點(diǎn)尋找完畢之后,剩余的節(jié)點(diǎn)則被認(rèn)為是沒有被引用到的節(jié)點(diǎn),即無用的節(jié)點(diǎn),無用的節(jié)點(diǎn)將會(huì)被判定為是可回收的對象。
引用節(jié)點(diǎn)圖論

在 Java 中,可作為 GC Roots 的對象包括下面幾種:
①虛擬機(jī)棧中引用的對象(棧幀中的本地變量表);
②方法區(qū)中類靜態(tài)屬性引用的對象;
③方法區(qū)中常量引用的對象;
④本地方法棧中 JNI(Native方法) 引用的對象。

無論是通過引用計(jì)數(shù)算法判斷對象的引用數(shù)量,還是通過可達(dá)性分析算法判斷對象的引用鏈?zhǔn)欠窨蛇_(dá),判定對象是否存活都與“引用”有關(guān)。在 Java 中,將引用又分為強(qiáng)引用、軟引用、弱引用、虛引用四種,這四種引用強(qiáng)度依次逐漸減弱。無論引用計(jì)數(shù)算法還是可達(dá)性分析算法都是基于強(qiáng)引用而言的。

3??對象死亡(被回收)前的最后一次掙扎

即使在可達(dá)性分析算法中不可達(dá)的對象,也并非是“非死不可”,這時(shí)候它們暫時(shí)處于“緩刑”階段,要真正宣告一個(gè)對象死亡,至少要經(jīng)歷兩次標(biāo)記過程。

第①次標(biāo)記:如果對象在進(jìn)行可達(dá)性分析后發(fā)現(xiàn)沒有與 GC Roots 相連接的引用鏈,那它將會(huì)被第一次標(biāo)記;
第②次標(biāo)記:第一次標(biāo)記后接著會(huì)進(jìn)行一次篩選,篩選的條件是此對象是否有必要執(zhí)行 finalize()。在 finalize() 中沒有重新與引用鏈建立關(guān)聯(lián)關(guān)系的,將被進(jìn)行第二次標(biāo)記。

第二次標(biāo)記成功的對象將真的會(huì)被回收,如果對象在 finalize() 中重新與引用鏈建立了關(guān)聯(lián)關(guān)系,那么將會(huì)逃離本次回收,繼續(xù)存活。

4??方法區(qū)如何判斷是否需要回收

方法區(qū)存儲(chǔ)內(nèi)容是否需要回收的判斷不同于上。方法區(qū)主要回收的內(nèi)容有:廢棄常量和無用的類。對于廢棄常量也可通過引用的可達(dá)性來判斷,但是對于無用的類則需要同時(shí)滿足下面三個(gè)條件:
①該類所有的實(shí)例都已經(jīng)被回收,也就是 Java 堆中不存在該類的任何實(shí)例;
②加載該類的 ClassLoader 已經(jīng)被回收;
③該類對應(yīng)的java.lang.Class對象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法。

五、常用的垃圾收集算法

1??標(biāo)記-清除算法

標(biāo)記-清除算法采用從根集合(GC Roots)進(jìn)行掃描,對存活的對象進(jìn)行標(biāo)記,標(biāo)記完畢后,再掃描整個(gè)空間中未被標(biāo)記的對象,進(jìn)行回收,如圖。該算法不涉及對象的移動(dòng),只處理不存活的對象,在存活對象比較多的情況下相對高效。但是會(huì)有兩個(gè)主要問題:①效率不高,標(biāo)記和清除的效率都很低;②會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片,導(dǎo)致以后程序在分配較大的對象時(shí),由于沒有充足的連續(xù)內(nèi)存而提前觸發(fā)一次 GC 動(dòng)作。
標(biāo)記-清除算法

2??復(fù)制算法【參見第三項(xiàng)】

復(fù)制算法的提出是為了克服句柄的開銷和解決內(nèi)存碎片的問題。它開始時(shí)把堆分成一個(gè)對象面和多個(gè)空閑面,程序從對象面為對象分配空間,當(dāng)對象滿了,基于復(fù)制算法的垃圾收集就從根集合(GC Roots)中掃描還活動(dòng)的對象,并將每個(gè)活動(dòng)對象復(fù)制到空閑面(使得活動(dòng)對象所占的內(nèi)存之間沒有空閑洞),這樣空閑面變成了對象面,原來的對象面變成了空閑面,程序會(huì)在新的對象面中分配內(nèi)存。然后一次性清除完第一塊內(nèi)存,再將第二塊上的對象復(fù)制到第一塊。但是這種方式,內(nèi)存的代價(jià)太高,每次基本上都要浪費(fèi)一半的內(nèi)存。于是將該算法進(jìn)行了改進(jìn),內(nèi)存區(qū)域不再是按照 1:1 去劃分,而是將內(nèi)存劃分為 8:1:1 三部分,較大那份內(nèi)存叫 Eden 區(qū),其余是兩塊較小的內(nèi)存區(qū)叫 Survior 區(qū)。每次都會(huì)優(yōu)先使用 Eden 區(qū),若 Eden 區(qū)滿,就將對象復(fù)制到第二塊內(nèi)存區(qū)上,然后清除 Eden 區(qū),如果此時(shí)存活的對象太多,以至于 Survivor 不夠時(shí),會(huì)將這些對象通過分配擔(dān)保機(jī)制復(fù)制到老年代中。
復(fù)制算法

3??標(biāo)記-整理算法

該算法采用標(biāo)記-清除算法一樣的方式進(jìn)行對象的標(biāo)記,但在清除時(shí)不同,在回收不存活的對象占用的空間后,會(huì)將所有的存活對象往左端空閑空間移動(dòng),并更新對應(yīng)的指針。該算法是在標(biāo)記-清除算法的基礎(chǔ)上,又進(jìn)行了對象的移動(dòng),因此成本更高,但是卻解決了內(nèi)存碎片的問題。當(dāng)對象存活率較高時(shí),也解決了復(fù)制算法的效率問題。具體流程如圖:
標(biāo)記-整理算法

六、垃圾收集器

如圖是 HotSpot 虛擬機(jī)包含的所有收集器:

新生代收集器:Serial、ParNew、Parallel Scavenge
老年代收集器:CMS、Serial Old、Parallel Old
新生代和老年代的收集器之間進(jìn)行連線,說明它們之間可以搭配使用。

七、新生代垃圾收集器

1??Serial 垃圾收集器---串行收集器【復(fù)制算法】
Serial 收集器是最基本的、發(fā)展歷史最悠久的收集器。

特點(diǎn):
串行收集器是指使用單線程進(jìn)行垃圾回收的收集器。每次回收時(shí),串行收集器只有一個(gè)工作線程,暫停其他線程。對于并行能力較弱的單 CPU 計(jì)算機(jī)來說,串行收集器的專注性和獨(dú)占性往往有更好的性能表現(xiàn)。它存在 Stop The World 問題,即垃圾回收時(shí),要停止程序的運(yùn)行。使用-XX:+UseSerialGC參數(shù)可以設(shè)置新生代使用這個(gè)串行收集器。

2??默認(rèn)ParNew 垃圾收集器---并行收集器【復(fù)制算法】
ParNew 其實(shí)就是 Serial 的多線程版本,除了使用多線程之外,其余參數(shù)和 Serial 一模一樣。

特點(diǎn):
ParNew 默認(rèn)開啟的線程數(shù)與 CPU 數(shù)量相同,在 CPU 核數(shù)很多的機(jī)器上,可以通過參數(shù)-XX:ParallelGCThreads來設(shè)置線程數(shù)。它是目前新生代首選的垃圾收集器,因?yàn)槌?ParNew 之外,它是唯一一個(gè)能與老年代 CMS 配合工作的。它同樣存在 Stop The World 問題。使用-XX:+UseParNewGC參數(shù)可以設(shè)置新生代使用這個(gè)并行收集器。

3??Parallel Scavenge 收集器
Parallel Scavenge 使用復(fù)制算法回收垃圾,也是多線程的。

特點(diǎn):
就是非常關(guān)注系統(tǒng)的吞吐量,吞吐量=代碼運(yùn)行時(shí)間/(代碼運(yùn)行時(shí)間+垃圾收集時(shí)間)

-XX:MaxGCPauseMillis:設(shè)置最大垃圾收集停頓時(shí)間,可用把虛擬機(jī)在GC停頓的時(shí)間控制在 MaxGCPauseMillis 范圍內(nèi),如果希望減少 GC 停頓時(shí)間可以將 MaxGCPauseMillis 設(shè)置的很小,但是會(huì)導(dǎo)致 GC 頻繁,從而增加了 GC 的總時(shí)間,降低了吞吐量。所以需要根據(jù)實(shí)際情況設(shè)置該值。

-Xx:GCTimeRatio:設(shè)置吞吐量大小,它是一個(gè)0到100之間的整數(shù),默認(rèn)情況下他的取值是99,那么系統(tǒng)將花費(fèi)不超過1/(1+n)的時(shí)間用于垃圾回收,也就是1/(1+99)=1%的時(shí)間。

另外還可以指定-XX:+UseAdaptiveSizePolicy打開自適應(yīng)模式,在這種模式下,新生代的大小、eden、from/to的比例,以及晉升老年代的對象年齡參數(shù)會(huì)被自動(dòng)調(diào)整,以達(dá)到在堆大小、吞吐量和停頓時(shí)間之間的平衡點(diǎn)。

使用-XX:+UseParallelGC參數(shù)可以設(shè)置新生代使用這個(gè)并行收集器。

八、老年代垃圾收集器

1??SerialOld 垃圾收集器---串行收集器【標(biāo)記 - 整理算法】
SerialOld 是 Serial 收集器的老年代收集器版本,它同樣是一個(gè)單線程收集器。

用途:
一個(gè)是在JDK1.5及之前的版本中與 Parallel Scavenge 收集器搭配使用。另一個(gè)就是作為 CMS 收集器的后備預(yù)案,如果 CMS 出現(xiàn) Concurrent Mode Failure,則 SerialOld 將作為后備收集器。

2??Parallel Old 垃圾收集器---并行收集器【標(biāo)記壓縮算法】
老年代 Parallel Old 垃圾收集器也是一種多線程的收集器,和新生代的 Parallel Scavenge 收集器一樣,也是一種關(guān)注吞吐量的收集器。使用-XX:+UseParallelOldGc設(shè)置老年代使用該收集器。使用-XX:+ParallelGCThreads設(shè)置垃圾收集時(shí)的線程數(shù)量。

3??CMS 垃圾收集器【標(biāo)記清除法】
CMS 全稱 Concurrent Mark Sweep 意為并發(fā)標(biāo)記清除。主要關(guān)注系統(tǒng)停頓時(shí)間。使用-XX:+UseConcMarkSweepGC設(shè)置老年代使用該收集器。使用-XX:ConcGCThreads設(shè)置垃圾收集時(shí)的線程數(shù)量。

特點(diǎn):
CMS 并不是獨(dú)占的收集器,也就說 CMS 回收的過程中,應(yīng)用程序仍然在不停的工作,又會(huì)有新的垃圾不斷的產(chǎn)生,所以在使用 CMS 的過程中應(yīng)該確保應(yīng)用程序的內(nèi)存足夠可用。

CMS 不會(huì)等到應(yīng)用程序飽和的時(shí)候才去回收垃圾,而是在某一閥值的時(shí)候開始回收,回收閥值可用指定的參數(shù)進(jìn)行配置:-XX:CMSInitiatingoccupancyFraction來指定,默認(rèn)為 68,也就是說當(dāng)老年代的空間使用率達(dá)到 68% 的時(shí)候,會(huì)執(zhí)行 CMS。

如果內(nèi)存使用率增長的很快,在 CMS 執(zhí)行的過程中,已經(jīng)出現(xiàn)了內(nèi)存不足的情況,此時(shí) CMS 回收就會(huì)失敗,虛擬機(jī)將啟動(dòng)老年代串行收集器 SerialOld GC 進(jìn)行垃圾回收,這會(huì)導(dǎo)致應(yīng)用程序中斷,直到垃圾回收完成后才會(huì)正常工作。這個(gè)過程 GC 的停頓時(shí)間可能較長,所以-XX:CMSInitiatingoccupancyFraction的設(shè)置要根據(jù)實(shí)際的情況。

標(biāo)記清除法有個(gè)缺點(diǎn)就是存在內(nèi)存碎片的問題,那么 CMS 有個(gè)參數(shù)設(shè)置-XX:+UseCMSCompactAtFullCollecion可以使 CMS 回收完成之后進(jìn)行一次碎片整理。使用-XX:CMSFullGCsBeforeCompaction設(shè)置進(jìn)行多少次 CMS 回收之后,對內(nèi)存進(jìn)行一次壓縮。

在 CMS GC 過程中,由于老年代剩余空間無法存放需要分配的對象,會(huì)產(chǎn)生concurrent-mode-failure。

九、堆內(nèi)存常見參數(shù)配置

十、TLAB 內(nèi)存

TLAB 全稱是 Thread Local Allocation Buffer 即線程本地分配緩存,從名字上看是一個(gè)線程專用的內(nèi)存分配區(qū)域,是為了加速對象分配而生的。每一個(gè)線程都會(huì)產(chǎn)生一個(gè) TLAB,該線程獨(dú)享的工作區(qū)域,JVM 使用這種 TLAB 區(qū)來避免多線程沖突問題,提高了對象分配的效率。TLAB 空間一般不會(huì)太大,當(dāng)大對象無法在 TLAB 分配時(shí),則會(huì)直接分配到堆上。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容