天天看點

記憶體溢出、記憶體洩露和FULL GC

記憶體溢出和記憶體洩露

記憶體溢出 out of memory,是指程式在申請記憶體時,沒有足夠的記憶體空間供其使用,出現out of memory;

比如:記憶體中加載的資料量過于龐大,如一次從資料庫取出過多資料;集合類中有對對象的引用,使用完後未清空,使得JVM不能回收;代碼中存在死循環或循環産生過多重複的對象實體等。

記憶體洩露 memory leak,是指程式在申請記憶體後,無法釋放已申請的記憶體空間,一次記憶體洩露危害可以忽略,但記憶體洩露堆積後果很嚴重,無論多少記憶體,遲早會被占光。長生命周期的對象持有短生命周期對象的引用就很可能發生記憶體洩露,盡管短生命周期對象已經不再需要,但是因為長生命周期對象持有它的引用而導緻不能被回收,故發生記憶體洩露。

比如:靜态集合類引起記憶體洩露,像HashMap、Vector等的使用最容易出現記憶體洩露,這些靜态變量的生命周期和應用程式一緻,他們所引用的所有的對象Object也不能被釋放,因為他們也将一直被Vector等引用着;當集合裡面的對象屬性被修改後,再調用remove()方法時不起作用;比如一個應用中使用多到監聽器,但往往在釋放對象的時候卻沒有記住去删除這些監聽器,進而增加了記憶體洩漏的機會。

兩者間的聯系:

記憶體洩露會最終會導緻記憶體溢出。

相同點:都會導緻應用程式運作出現問題,性能下降或挂起。

不同點:

1) 記憶體洩露是導緻記憶體溢出的原因之一,記憶體洩露積累起來将導緻記憶體溢出。

2) 記憶體洩露可以通過完善代碼來避免,記憶體溢出可以通過調整配置來減少發生頻率,但無法徹底避免

JVM出現Full GC的情況

從新生代空間(包括 Eden 和 Survivor 區域)回收記憶體被稱為 Minor GC,對老年代GC稱為Major GC,而Full GC是對整個堆來說的,Major GC的速度一般會比Minor GC慢10倍以上。

記憶體溢出、記憶體洩露和FULL GC

1、System.gc()方法的調用

程式員通過函數調用的形式,讓JVM進行一次full gc的操作。盡量避免人工去進行full gc,最好讓JVM自己去回收。

2、老年代空間不足

老年代空間的對象有兩種形式,一種是有新生代滿足minor gc次數後進入到老年代;另一種是建立了大的對象、大的數組,不經過新生代直接到老年代的形式。當老年代空間不足是就會觸發full gc的情況。

不要建立過大的對象及數組,讓對象在新生代中回收可以解決這種情況。

3、當新生代中要轉移到老年代中的對象的大小低于老年代的剩餘空間時

Hotspot為了避免由于新生代對象晉升到老年代導緻老年代空間不足的現象,在進行Minor GC時,做了一個判斷,如果之前統計所得到的Minor GC晉升到老年代的平均大小大于老年代的剩餘空間,那麼就直接觸發Full GC。

4、方法區空間不足

運作時資料區域中的方法區用來存放類的資訊、常量、靜态變量等資料,當系統中要加載的類、反射的類和調用的方法較多時可能會被占滿,在未配置為采用CMS GC的情況下也會執行Full GC。

可增大方法區空間或轉為使用CMS GC。

5、CMS GC(Concurrent Mark Sweep GC)時出現promotion failed和concurrent mode failure

promotion failed是在進行Minor GC時,survivor space放不下、對象隻能放入老年代,而此時老年代也放不下造成的;concurrent mode failure是在執行CMS GC的過程中同時有對象要放入老年代,而此時老年代空間不足造成的

可以增大survivor空間、老年代空間或調低觸發并發GC的比例。

6、堆中配置設定很大的對象

這裡的大對象是指需要大量連續記憶體空間的java對象,例如很長的數組,此種對象會直接進入老年代,而老年代雖然有很大的剩餘空間,但是無法找到足夠大的連續空間來配置設定給目前對象,此種情況就會觸發JVM進行Full GC。

可通過配置CMS垃圾收集UseCMSCompactAtFullCollection開關參數。

總結:

Full GC可以清除老年代的垃圾本質上是好的行為,但是如果Full GC發生的頻率高了,就會影響性能。同時,Full GC頻繁發生,意味着你的記憶體配置設定機制存在問題,也許是記憶體洩露,有大量記憶體垃圾不斷在老年代産生;也許是你的大對象(緩存)過多;也有可能是你的參數設定不好,minor GC清理不掉記憶體,導緻每次minor GC都會觸發Full GC;還有可能是你的老年代大小參數設定錯誤,老年代過小等等原因,合理排查處理才能最優化自己的代碼。