1. 程式人生 > >jvm:Minor GC與Full GC分別在什麼時候發生?

jvm:Minor GC與Full GC分別在什麼時候發生?

首先區分一下Minor GC和Full GC:


Minor GC是新生代GC,指的是發生在新生代的垃圾收集動作。由於java物件大都是朝生夕死的,所以Minor GC非常頻繁,一般回收速度也比較快。

Major GC/Full GC 是老年代GC,指的是發生在老年代的GC,出現Major GC一般經常會伴有Minor GC,Major GC的速度比Minor GC慢的多。

觸發MinorGC(Young GC)

虛擬機器在進行minorGC之前會判斷老年代最大的可用連續空間是否大於新生代的所有物件總空間

    1、如果大於的話,直接執行minorGC

    2、如果小於,判斷是否開啟HandlerPromotionFailure,沒有開啟直接FullGC

    3、如果開啟了HanlerPromotionFailure, JVM會判斷老年代的最大連續記憶體空間是否大於歷次晉升的大小,如果小於直接執行FullGC

    4、如果大於的話,執行minorGC

觸發FullGC

  • 老年代空間不足

     如果建立一個大物件,Eden區域當中放不下這個大物件,會直接儲存在老年代當中,如果老年代空間也不足,就會觸發Full GC。為了避免這種情況,最好就是不要建立太大的物件。

  • 持久代空間不足

    如果有持久代空間的話,系統當中需要載入的類,呼叫的方法很多,同時持久代當中沒有足夠的空間,就出觸發一次Full GC

  • YGC出現promotion failure

    promotion failure發生在Young GC, 如果Survivor區當中存活物件的年齡達到了設定值,會就將Survivor區當中的物件拷貝到老年代,如果老年代的空間不足,就會發生promotion failure, 接下去就會發生Full GC.

  • 統計YGC發生時晉升到老年代的平均總大小大於老年代的空閒空間

      在發生YGC是會判斷,是否安全,這裡的安全指的是,當前老年代空間可以容納YGC晉升的物件的平均大小,如果不安全,就不會執行YGC,轉而執行Full GC。

  • 顯示呼叫System.gc

==========================================================================================

Minor GC:

Eden區域滿了,或者新建立的物件大小 > Eden所剩空間

CMS設定了CMSScavengeBeforeRemark引數,這樣在CMS的Remark之前會先做一次Minor GC來清理新生代,加速之後的Remark的速度。這樣整體的stop-the world時間反而短

Full GC的時候會先觸發Minor GC。執行Minor GC需要注意:

>>A.當JVM無法為一個新的物件分配空間時會觸發Minor GC,比如當Eden區滿了。所以分配率越高,越頻繁執行Minor GC。

>>B.記憶體池被填滿的時候,其中的內容全部會被複制,指標會從0開始跟蹤空閒記憶體。Eden和Survivor區進行了標記和複製操作,取代了經典的標記、掃描、壓縮、清理操作。所以Eden 和Survivor 區不存在記憶體碎片。寫指標總是停留在所使用記憶體池的頂部。

>>C.執行Minor GC操作時,不會影響到永久代。從永久代到年輕代的引用被當成GC roots,從年輕代到永久代的引用在標記階段被直接忽略掉。

>>D.質疑常規的認知,所有的Minor GC都會觸發“全世界的暫停(stop-the-world)”,停止應用程式的執行緒。對於大部分應用程式,停頓導致的延遲都是可以忽略不計的。其中的真相就是,大部分Eden 區中的物件都能被認為是垃圾,永遠也不會被複制到Survivor 區或者老年代空間。如果正好相反,Eden 區大部分新生物件不符合GC條件,Minor GC 執行時暫停的時間將會長很多。

Major GC:清理永久代,但是由於很多MojorGC 是由MinorGC 觸發的,所以有時候很難將MajorGC 和MinorGC區分開。

FullGC:是清理整個堆空間—包括年輕代和永久代。FullGC 一般消耗的時間比較長,遠遠大於MinorGC,因此,有時候我們必須降低FullGC 發生的頻率。

Minor GC後存活的物件晉升到老年代時由於悲觀策略的原因,有兩種情況會觸發Full GC, 1種是之前每次晉升的物件的平均大小 > 老年代剩餘空間

1種是Minor GC後存活的物件超過了老年代剩餘空間。這兩種情況都是因為老年代會為新生代物件的晉升提供擔保,而每次晉升的物件的大小是無法預測的,所以只能基於統計,1個是基於歷史平均水平,一個是基於下一次可能要晉升的最大水平。這兩種情況都是屬於promotion failure

CMS失敗,發生concurrent mode failure會引起Full GC,這種情況下會使用Serial Old收集器,是單執行緒的,對GC的影響很大。concurrent mode failure產生的原因是老年代剩餘的空間不夠,導致了和gc執行緒併發執行的使用者執行緒建立的大物件(由PretenureSizeThreshold控制新生代直接晉升老年代的物件size閥值)不能進入到老年代,只要stop the world來暫停使用者執行緒,執行GC清理。可以通過設定CMSInitiatingOccupancyFraction預留合適的CMS執行時剩餘的空間

新生代直接晉升到老年代的大物件超過了老年代的剩餘空間,引發Full GC。注意於promotion failure的區別,promotion failure指的是Minor GC後發生的擔保失敗

Perm永久代空間不足會觸發Full GC,可以讓CMS清理永久代的空間。設定CMSClassUnloadingEnabled即可

System.gc()引起的Full GC,可以設定DisableExplicitGC來禁止呼叫System.gc引發Full GC


=========================================================================

虛擬機器給每個物件定義了一個物件年齡(Age)計數器。如果物件在 Eden 出生並經過第一次 Minor GC 後仍然存活,並且能被 Survivor 容納的話,將被移動到 Survivor 空間中,並將物件年齡設為 1。物件在 Survivor 區中每熬過一次 Minor GC,年齡就增加 1 歲,當它的年齡增加到一定程度(預設為 15 歲)時,就會被晉升到老年代中。物件晉升老年代的年齡閾值,可以通過引數 -XX:MaxTenuringThreshold (閾值)來設定。