目錄
一、背景
二、三者之間的區分
1、Minor GC
2、Major GC
(1)老年代空間不足:
(2)晉升(Promotion)失敗:
(3)空間分配擔保失敗:
(4)顯式調用:
3、Full GC
三、總結
四、JVM調優參數分享
一、背景
在我們工作中,雖然我們更多的是寫代碼的,但是Java的內存機制我們其實是無時無刻都在打交道,項目運行要多少內存評估,上線后是否能抗住流量,Java內存中新生代、老年代的比例設置比例是多少等等。這次主要是講講我們常接觸的GC中的Minor GC、Major GC、FullGC到底有什么區別以及在工作中應該如何注意減少GC的頻率
二、三者之間的區分
1、Minor GC
Minor GC,它的全稱是(Young Generation Garbage Collection)是指對年輕代(Young Generation)進行的垃圾回收操作。所以說白了其實就是對年輕代的區域進行回收
在Java虛擬機中,堆內存被劃分為不同的區域,其中年輕代是對象分配的主要區域。Minor GC主要負責回收年輕代的垃圾對象。
年輕代通常分為三個區域:Eden區和兩個Survivor區(一般稱為From區和To區)。當對象被創建時,它們會被分配到Eden區。在年輕代的垃圾回收過程中,首先會對Eden區進行垃圾回收,將存活的對象復制到一個空閑的Survivor區中(通常是To區),同時清空Eden區。如果Survivor區無法容納所有存活的對象,一部分對象會被直接晉升到老年代(Tenured Generation)。
所以我們的Minor GC 在我們的程序運行的過程中其實是很頻繁的,因為我們的代碼如一些接口、消費者類,定時任務等都會很頻繁的創建對象和銷毀對象,這些對象大部分在新生代里面,有朝生夕死的特點在多次Minor GC后,存活時間較長的對象會逐漸被移到Survivor區,并經過多次復制和清理的過程。當對象經歷了一定次數的復制后,會被認為是長時間存活的對象,最終會被晉升到老年代。
總結:Minor GC通常是并行或并發執行的,意味著在垃圾回收期間,應用程序的執行可能會暫停或降低速度。為了減少這種停頓時間,一些垃圾回收器,如并行垃圾回收器(Parallel GC)和G1垃圾回收器(Garbage-First GC),采用了并發標記和清理的方式。
總之,Minor GC是一種針對年輕代進行的垃圾回收操作,主要目的是回收年輕代的垃圾對象,以保證堆內存的有效利用和應用程序的性能。所以總而來說Minor GC在我們的應用程序的運行過程中,確實是比較頻繁的操作,大家如果看到MinorGC 很頻繁,也不必過分驚慌。
2、Major GC
什么是Major GC?
Major GC的全稱是(Major Garbage Collection)是指對Java虛擬機中的老年代(Tenured Generation)進行的垃圾回收操作。與Minor GC主要關注年輕代的回收不同,Major GC專注于回收老年代中的垃圾對象。
所以其實你可以理解為和Minor GC的區別是前者是專門用于年輕代對象回收的機制,后者是用于老年代的對象回收。
在Java堆內存中,老年代用于存放生命周期較長的對象或者經過多次Minor GC后仍然存活的對象。隨著時間的推移,老年代中的垃圾對象會逐漸增加,因此需要進行周期性的垃圾回收來釋放這些占用的內存空間。
Major GC的觸發條件通常是由JVM自動管理的,具體條件可能因不同的JVM實現和垃圾回收器而有所不同。它可能在以下情況下觸發:
1. 老年代空間不足:當老年代無法容納新對象或晉升對象時,會觸發Major GC來回收老年代的垃圾對象。
2、永久代垃圾回收:如果使用的是傳統的垃圾回收器(如Parallel GC、CMS等),那么Major GC也會包含對永久代(Permanent Generation)的垃圾回收操作,用于清理無效的類定義、常量等。
3、JVM顯式調用:通過System.gc()或Runtime.getRuntime().gc()等方式顯式調用垃圾回收,可能會觸發Major GC。
Major GC的執行時間一般比Minor GC更長,因為它需要處理較多的對象和進行更復雜的內存整理操作。在Major GC期間,應用程序的執行將會暫停,直到垃圾回收操作完成。了解Major GC對于應用程序的性能分析和調優非常重要,適當配置堆大小、調整垃圾回收器參數等可以減少Major GC的頻率和停頓時間,以提高應用程序的吞吐量和響應性能。
2、再完整總結下,什么情況下會觸發Major GC?
Major GC(Major Garbage Collection)在Java虛擬機中會在以下情況下被觸發:
(1)老年代空間不足:
當老年代(Tenured Generation)無法容納新對象或晉升對象時,會觸發Major GC來回收老年代的垃圾對象。這種情況通常發生在頻繁創建大對象或者持久對象導致老年代空間快要滿了的情況下。(這個應該是比較常見的一種現象,隨著我們的程序運行的越來越久,生成的老年代的對象越來越多,可能就會觸發Major GC來進行空間回收)
(2)晉升(Promotion)失敗:
在年輕代(Young Generation)中的對象經過多次Minor GC后仍然存活并且達到了晉升的條件,但是老年代空間不足以容納它們時,也會觸發MajorGC。這種情況可能是因為年輕代中的對象生命周期較長,導致垃圾對象聚集在老年代中。(如果發生這種情況,我們應該注意,說明是不是我們程序是否有一些高頻的創建對象的行為,并且是較大的對象,導致大量大對象往老年代遷移,具體的體現行為例如有:接口流量激增,定時任務查詢的數據庫數據過多,大數據量導出等,觀察項目的運行情況有利于我們找到頻繁Marjor GC的原因)
(3)空間分配擔保失敗:
在進行Minor GC時,如果老年代的連續內存空間不足以容納晉升對象,JVM會嘗試進行一次Minor GC并且通過移動對象來釋放更多的連續空間。如果這個過程之后仍然無法滿足空間需求,那么會觸發Major GC。
(4)顯式調用:
通過System.gc()或Runtime.getRuntime().gc()等方式顯式調用垃圾回收,也有可能觸發Major GC。不過請注意,Java虛擬機對于顯式調用垃圾回收的處理是可選的,因此并不保證一定會觸發Major GC。
總結:具體的Major GC觸發條件可能因不同的JVM實現和垃圾回收器而有所不同。此外,Major GC的具體行為和執行策略也會受到所使用的垃圾回收器的影響。因此,在實際應用中,可以通過調整堆大小、調整垃圾回收器參數等方式來影響Major GC的觸發頻率和行為,以優化應用程序的性能。
3、Full GC
Full GC(Full Garbage Collection)是Java虛擬機(JVM)中的一種垃圾回收操作。它是指對整個堆內存進行回收,包括新生代和老年代。如果一旦遇到Full GC那一定得重視起來,因為可能程序Full GC的頻率很高就會容易導致程序不穩定甚至崩潰。
在Java中,垃圾回收器通常會將堆內存劃分為不同的區域,如新生代和老年代。當新生代空間不足時,會觸發Minor GC,只清理新生代內存。而當老年代空間不足或者為了整理碎片化的內存,就有可能會觸發Full GC,對整個堆內存進行回收。同時永久代(元空間)不足也有可能觸發Full GC。本人就有一次在公司遇到頻繁Full GC,就是因為元空間不足導致,調大了元空間的初始化內存即可進行優化。
Full GC 可能會導致較長的停頓時間,因為它需要掃描整個堆內存,標記可回收對象,并進行內存整理。這意味著在 Full GC 過程中,應用程序的執行會被暫停。
Full GC 的頻率會受多種因素影響,如堆內存的大小、JVM配置參數、對象分配速度等。如果 Full GC 發生過于頻繁或耗時過長,可能會導致應用程序的性能下降。所以一旦遇到頻繁的Full GC一定要重視起來
為了減少 Full GC 的頻率和時間,我們可以采取以下策略:
- 調整堆內存大小:適當設置堆內存大小,避免過小的情況。可以在一開始的時候就設置好初始化內存和最大內存一致,防止內存抖動導致應用程序不穩定。
- 設置堆內存
-
初始堆大小?(
-Xms
): 指定JVM啟動時初始分配的堆內存大小。 -
最大堆大小?(
-Xmx
): 指定JVM可以使用的最大堆內存大小。
-
- 設置堆內存
- 優化對象分配:在代碼里面能復用的對象盡量復用,不過頻繁的創建對象,這樣會導致程序不穩定以及觸發Full GC。
- 盡可能使用較新的垃圾回收器,有條件用G1的就用G1,或者選擇CMS,過老的垃圾回收器性能較差。
- 進行代碼優化:減少內存泄漏和不必要的對象引用,使垃圾回收更高效。
三、總結
Minor GC(Young Generation Garbage Collection)和Major GC(Major Garbage Collection)、Full GC(Full Garbage Collection)都是Java虛擬機中的垃圾回收操作,但它們的執行對象和目的略有不同。
Minor GC主要針對年輕代(Young Generation)進行垃圾回收,即對Eden區和Survivor區進行清理。它的目的是回收年輕代的垃圾對象,以保證堆內存的有效利用和應用程序的性能。Minor GC通常頻繁發生,但每次垃圾回收的停頓時間較短。
而Full GC是對整個堆內存進行垃圾回收,包括年輕代和老年代(Tenured Generation)。Full GC的觸發條件相對復雜,通常情況下會在以下幾種情況下觸發:當年輕代無法容納對象、永久代(Permanent Generation)滿了。Full GC的目的是回收整個堆內存中的垃圾對象,并進行一些更為耗時的操作,如處理永久代中的無效類、對堆內存進行碎片整理等。因此,Full GC通常會導致較長的停頓時間,對應用程序的性能會產生比較大的影響。
四、JVM調優參數分享
這里分享幾個JVM調優的參數,都是基于實際的場景并且經過驗證、現在生產環境就是這樣配置
-XX:+UseContainerSupport 基于容器化啟動,參數允許JVM自動根據容器的資源限制來調整其內部資源分配,這樣可以更好地利用容器的資源,同時避免因資源分配不當導致的性能問題。 容器化啟動必備
-XX:MaxRAMPercentage=40.0 容器化JVM內存分配比例,假設容器是4g,則將JVM內存設置為1.6g
-XX:InitialRAMPercentage=40.0 同上
-XX:MinRAMPercentage=40.0 同上
-XX:MetaspaceSize=512m 元空間內存大小
-XX:MaxMetaspaceSize=512m 最大之所以設置和初始化一樣,就是防止元空間自增導致的系統的抖動
-XX:+UseG1GC 使用G1垃圾回收器
-XX:+PrintGCDetails 打印GC日志,也是相當實用的一個配置
-XX:+PrintHeapAtGC 在GC的時候打印堆信息,用于在GC的時候輔助分析問題,也是比較實用
-XX:+PrintGCDateStamps 輸出gc時間
-Xloggc:/app/logs/myapplication-%t.gclog gc日志位置
-XX:+HeapDumpOnOutOfMemoryError 用于當虛擬機出現內存溢出異常時,Dump出當前的內存堆轉存快照以便事后分析
-XX:+ExitOnOutOfMemoryError JVM將在拋出OutOfMemoryError時立即退出,用于內存溢出時退出應用,以便容器重啟應用,相當實用的一個配置
-XX:HeapDumpPath=/app/dump/myapplications.hprof dump文件地址
其實可以看到這里并沒有太多的參數調優,實際情況下,JVM已經把垃圾回收器做得足夠好了,很多時候它的默認參數已經是比較好的調優了,這里只是設置了一些堆的大小,包括記錄一些內存溢出或者GC時的一些信息,方便分析問題。