java.lang.OutOfMemoryError:GC overhead limit exceeded

java.lang.OutOfMemoryError這個錯誤是比較經典的錯誤了,經過JDK不斷的迭代,服務器硬件的不斷升級。。。總之,社會在發展,時代在進步。很多錯誤已經消失在時代的浪潮中。我也是很久沒有見過這個錯誤了,以至於都以爲在Java的世界中不會再碰到這個錯誤。結果,就在最疏忽的時候碰到了TA,真是,心中一萬隻神獸奔襲而過,狠狠的踐踏了我這顆上了年紀的心臟啊。

吐槽完畢,言歸正傳。

Java剛剛出現的年代,有一個相比於其他語言的優勢就是,內存回收機制。不需要明確的調用釋放內存的API,java就自動完成,這個過程就是Garbage Collection,簡稱GC。這對以懶著稱的程序猿們來說,絕對是重大利好。但是,凡事有利必有弊,可以肯定的是,Java語言是人創造的,GC也是人編寫的代碼,絕對不是機器自動完成的。也就是說,GC的過程是另外一羣程序猿根據可能出現的情況,預設了GC條件,把符合回收條件的內存空間釋放出來。一旦被佔用的內存空間不符合釋放的條件,GC沒辦法清理,那就會適時出現java.lang.OutOfMemoryError。這個錯誤就是提醒我們這羣程序猿,寫GC程序的程序猿不知道這種情況怎麼處理,爲了安全也不便處理,誰使用Java就自己看着解決吧。

說起來,java.lang.OutOfMemoryError有幾種分類的,這次碰到的是java.lang.OutOfMemoryError: GC overhead limit exceeded,下面就來說說這種類型的內存溢出。

簡單來說,java.lang.OutOfMemoryError: GC overhead limit exceeded發生的原因是,當前已經沒有可用內存,經過多次GC之後仍然沒能有效釋放內存。

1. 原因

衆所周知,JVM的GC過程會因爲STW,只不過停頓短到不容易感知。當引起停頓時間的98%都是在進行GC,但是結果只能得到小於2%的堆內存恢復時,就會拋出java.lang.OutOfMemoryError: GC overhead limit exceeded這個錯誤。Plumbr給出一個示意圖:

java.lang.OutOfMemoryError: GC overhead limit exceeded

這個錯誤其實就是空閒內存與GC之間平衡的一個限制,當經過幾次GC之後,只有少於2%的內存被釋放,也就是很少的空閒內存,可能會再次被快速填充,這樣就會觸發再一次的GC。這就是一個惡性循環了,CPU大部分的時間在做GC操作,沒有時間做具體的業務操作,可能幾毫秒的任務需要幾分鐘都無法完成,整個應用程序就形同虛設了。

2. 示例

Plumbr上找的一個例子,這裏直接給出。

class Wrapper {
  public static void main(String args[]) throws Exception {
    Map map = System.getProperties();
    Random r = new Random();
    while (true) {
      map.put(r.nextInt(), "value");
    }
  }
}

然後設定堆內存是100m,比如java -Xmx100m -XX:+UseParallelGC Wrapper。不同的系統環境可能需要設置不同的堆內存大小,否則會產生不同的OOM錯誤。其實也算是好理解,因爲java.lang.OutOfMemoryError: GC overhead limit exceeded需要有兩個條件:98%的時間和2%的內存。如果這兩個條件有一個沒有達到,結果Map對象擴容,那就可能出現java.lang.OutOfMemoryError: Java heap space這個錯誤。

3. 解決方法

3.1 JVM參數

JVM給出一個參數避免這個錯誤:-XX:-UseGCOverheadLimit

但是,這個參數並不是解決了內存不足的問題,只是將錯誤發生時間延後,並且替換成java.lang.OutOfMemoryError: Java heap space

3.2 堆內存

還有一個偷懶的方法是:增大堆內存。既然堆內存少了,那就增加堆內存即可。

但是,這個方法也不是萬能的。因爲程序裏可能有內存泄露。這個時候即使再增大堆內存,也會有用完的時候。

所以前兩個方法都只是治標不治本而已。

3.3 終極方法

其實還是有一個終極方法的,而且是治標治本的方法,就是找到佔用內存大的地方,把代碼優化了,就不會出現這個問題了。

怎麼找到需要優化的代碼呢?就是通過heap dump生產jvm快照,通過分析快照找到佔用內存大的對象,從而找到代碼位置。

通過設置-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heapdump參數來生產快照,然後通過VisualVM或者MAT等工具分析快照內容進行定位。通過這個參數是將發生OOM時的堆內存所有信息寫入快照文件,也就是說,如果此時堆內存中有敏感信息的話,那就可能造成信息泄漏了。


個人主頁: http://www.howardliu.cn

個人博文: java.lang.OutOfMemoryError:GC overhead limit exceeded

CSDN主頁: http://blog.csdn.net/liuxinghao

CSDN博文: java.lang.OutOfMemoryError:GC overhead limit exceeded

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章