關于java.lang.OutOfMemoryError知多少(一)

概況

在實際的java應用中,我們可能會碰到成千上萬中java.lang.OutOfMemoryError的,不過總體癥狀體現往往在如下8種情況中

一、java.lang.OutOfMemoryError之java heap space

1、簡述

一般呢,java應用程序只能使用有限的內存,常常是在應用啟動的時候來指定的比如參數-Xmx和-XX:MaxPermSize。內存會被劃分為兩個區域:heap(堆空間)和 permgen(永久代)


內存構成.png

對應區域的大小是在Java虛擬機(JVM)啟動期間設置的,可以通過指定JVM參數- xmx和- xx:MaxPermSize來定制。若沒有顯式地設置大小,則使用特定于平臺的默認值。
往往出現java.lang.OutOfMemoryError,是由于應用程序試圖向堆空間區域添加更多數據時,卻沒有足夠的空間,便會觸發Java堆空間錯誤。

注意:即使當前機器可能有很多物理內存可用,JVM達到堆大小限制時,仍然會出現java.lang.OutOfMemoryError,拋出Java堆空間錯誤。

2、原因

對應上面出現的問題,是什么原因所致?接下來我們來剖析下:
比較常見的當我們常見將需要XXL大小的應用放入到S大小的java堆空間,中,由于應用所需空間不足,應用不能運行,導致OutOfMemoryError 。
除此之外其他程序錯誤的導致OutOfMemoryError的原因比較復雜:

  • 流量/數據峰值
    應用程序自身的處理存在一定的限額,比如一定數量的用戶或一定數量的數據。而當用戶數量或數據量突然激增并超過預期的閾值時,那么就會峰值停止前正常運行的操作將停止并觸發java . lang.OutOfMemoryError:Java堆空間錯誤
  • 內存泄漏
    編程中存在的缺陷錯誤會導致應用程序不斷消耗更多內存。每次使用該應用程序,都會將一些對象留到Java堆空間中。隨著時間的推移,泄漏的對象消耗掉所有可用的Java堆空間,并觸發已經熟悉的Java . lang.OutOfMemoryError:Java堆空間錯誤
3、實例

1、簡單實例
分配2M大小int[],當我們通過java -Xmx12m OOM則會出現Java . lang.OutOfMemoryError; 而一旦將java堆空間設置為13M則不會出現OutOfMemoryError

class OOM {
  static final int SIZE=2*1024*1024;
  public static void main(String[] a) {
    int[] i = new int[SIZE];
   }
}

關于數組內存大小計算:
首先知道java對象內存構成 = 對象頭(Header) + 實例數據(Instance Data)+ 對齊填充(Padding)
-對象頭在32位系統上占用8bytes,64位系統上占用16bytes
-引用類型在32位系統上每個占用4bytes, 在64位系統上每個占用8bytes;

-HotSpot的對齊方式為8字節對齊
-開啟(-XX:+UseCompressedOops)會導致指針壓縮,導致大小不一樣,詳情請自行學習《深入理解Java虛擬機》
上例:計算一維數組總內存 = 對象頭(24bytes) + 4bytes * 210241024

>javac OOM.java
>java -Xmx12m OOM
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at OOM.main(OOM.java:4)
>java -Xmx13m OOM

2、內存泄漏
在Java中,開發人員創建并使用新對象(例如new Integer(5))時,不必自己分配內存——這是由Java虛擬機(JVM)負責的。在應用程序的生命周期中,JVM會定期檢查內存中哪些對象仍在使用,哪些不是。未使用的對象可以被丟棄,內存被回收再利用。這個過程稱為垃圾收集。負責收集的JVM中對應的模塊稱為垃圾收集器(GC)。
Java的自動內存管理依賴于GC周期性地查找未使用的對象并刪除它們。簡單地說,Java中的內存泄漏是一些對象不再被應用程序使用但垃圾收集無法識別的情況。因此,這些未使用的對象仍然在Java堆空間中無限期地存在。不停的堆積最終會觸發java . lang.OutOfMemoryError

class KeylessEntry { 
   static class Key {
      Integer id; 
      Key(Integer id) {
         this.id = id;
      } 
      @Override
      public int hashCode() {
         return id.hashCode();
      }
   } 
   public static void main(String[] args) {
      Map m = new HashMap();
      while (true)
         for (int i = 0; i < 10000; i++)
            if (!m.containsKey(new Key(i)))
               m.put(new Key(i), "Number:" + i);
   }
}

當執行上面的代碼時,可能會期望它永遠運行,不會出現任何問題,假設單純的緩存解決方案只將底層映射擴展到10,000個元素,而不是所有鍵都已經在HashMap中。然而事實上元素將繼續被添加,因為key類并沒有重寫它的hashCode()和equals()。
隨著時間的推移,隨著不斷使用的泄漏代碼,“緩存”的結果最終會消耗大量Java堆空間。當泄漏內存填充堆區域中的所有可用內存時,垃圾收集無法清理它,java . lang.OutOfMemoryError。
相對來說對應的解決方案比較簡單:重寫equals方法

@Override
public boolean equals(Object o) {
   boolean response = false;
   if (o instanceof Key) {
      response = (((Key)o).id).equals(this.id);
   }
   return response;
}

二、java.lang.OutOfMemoryError之GC overhead limit exceeded

1、簡述

Java運行時環境包含一個內置的垃圾收集(GC)過程。在許多其他編程語言中,開發人員需要手動地分配和釋放內存區域,以便可以重用釋放的內存。
另外,Java應用程序只需要分配內存。每當內存中的某個特定空間不再使用時,一個稱為“垃圾收集”的單獨進程就會為它們清除內存。
當應用程序耗盡所有可用內存時,GC開銷限制超過了錯誤,而GC多次未能清除它,這時便會引發java.lang.OutOfMemoryError

2、原因

當JVM花費大量的時間執行GC,而收效甚微,而一旦整個GC的過程超過限制便會觸發錯誤。默認的jvm配置GC的時間超過98%,回收堆內存低于2%。


實例圖.png

若是JVM沒有設置上圖的限制,會帶來什么樣的后果:意味著GC能夠清理的少量堆將很快再次被填充,迫使GC再次重新啟動清洗過程。這形成了一個惡性循環,CPU 100%忙于GC,沒有實際工作可做。應用程序的最終用戶面臨極度的減速——通常以毫秒為單位的操作需要幾分鐘才能完成。
因此," java. lang.OutOfMemoryError:GC開銷上限“是一個很好的快速失敗的例子

3、實例
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");
    }
  }
}

當我們執行 java -Xmx100m -XX:+UseParallelGC Wrapper
由于上述代碼將不會被停止,很快我們就會得到java.lang.OutOfMemoryError:GC開銷超過限額;不過該實例在不同堆大小或不同GC算法會得到不同的結果:
(1)、應用程序將拋出常見的java . lang.OutOfMemoryError及Java堆空間消息。
(2)、采用其他垃圾收集算法(比如- xx:+ UseConcMarkSweepGC或- xx:+ UseG1GC)運行它時,錯誤被默認的異常處理程序捕獲,并且沒有stacktrace,由于堆已經耗盡,stacktrace甚至不能填充異常中
在資源受限的情況下,無法預測應用程序將如何掛掉,因此不能將期望基于特定的操作序列來完成

三、解決方案

1、在某些情況下,可能由于分配給JVM的堆的數量不足以滿足運行在JVM上的應用程序的需求。在這種情況下,一般是分配更多的堆來嘗試解決下。指定JVM參數-Xmx的值
java -Xmx1073741824 XXX_Class
java -Xmx1048576k XXX_Class
java -Xmx1024m XXX_Class
java -Xmx1g XXX_Class
2、在許多情況下,提供更多的Java堆空間并不能解決問題。例如應用程序包含內存泄漏,添加更多堆將延遲java . lang.OutOfMemoryError:Java堆空間錯誤。此外,增加Java堆空間的數量也增加了影響應用程序吞吐量或延遲的GC暫停時間。
如果希望解決Java堆空間的底層問題,而不是掩蓋這些癥狀,那么需要找出代碼中哪個部分負責分配最多的內存.需要搞懂兩點
(1)、哪個對象占據了大部分堆內存
(2)、在代碼什么地方分配了這些對象
操作的過程大致如下:

  • 首先獲得安全性許可,以便獲取JVM執行heap dump。“dumps”基本上是堆內容的快照便于分析。這些快照可以包含機密信息,如密碼、信用識別碼等,因此,由于安全原因,導致不能獲取這些dumps。
  • 其次要在合適的時刻獲取dump;一旦獲取dump的時刻不合適會有很多干擾信息存在dump文件里面不便于我們的分析。另外一個事實是獲取dump時會“暫停”JVM,影響應用性能
  • 另外找一臺機器用來分析這些dumps,并且結合當前這些jvm故障點使用heap大小,分析機器需要提供超過對應的heap大小heap來完成分析。這時我們可以使用專門的工具比如 [Eclipse MAT]、【JProfiler】甚至其他的專業軟件。
  • 檢測堆的最大占用者的GC根路徑,這塊對初學者存在一定的難度,不過我們嘗試下
  • 最后結合前面分析的結果,弄清楚在您的源代碼中,潛在危險的大量對象被分配到哪里【需要對自身的代碼比較熟悉,這個過程相對比較簡單的】
    如下是采用[Plumbr]分析個結果


    Plumbr分析.png

    分析結果如下:
    (1)、哪些對象正在消耗最多的內存(271個 com.example.map.impl.PartitionContainer實例消耗248MB總堆內存中的173MB。
    (2)這些對象在什么地方分配的(其中大部分[99%]分配在MetricManagerImpl類,第304行)
    (3)、當前引用這些對象的內容(完整的引用鏈到GC根)
    有了這些信息,就可以將定位底層的根源,并確保數據結構被縮減到合適內存使用級別。
    如果當從內存分析或閱讀Plumbr報告得出的結論是,內存使用是合法的,并且在源代碼中沒有任何更改,這時就需要允許提供更多地運行Java堆空間給JVM。在這種情況下,就調整JVM啟動配置,并添加(或增加當前的值), 類似-Xmx1024m

四、其他

最后給各位推薦一個阿里JVM大神寒泉子的公眾號(lovestblog)及其開發的JVM參數小程序(JVMPcoket)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,621評論 2 380

推薦閱讀更多精彩內容

  • 從三月份找實習到現在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發崗...
    時芥藍閱讀 42,339評論 11 349
  • 1.什么是垃圾回收? 垃圾回收(Garbage Collection)是Java虛擬機(JVM)垃圾回收器提供...
    簡欲明心閱讀 89,764評論 17 311
  • 這篇文章是我之前翻閱了不少的書籍以及從網絡上收集的一些資料的整理,因此不免有一些不準確的地方,同時不同JDK版本的...
    高廣超閱讀 15,668評論 3 83
  • 故黃河橋上交警攔住一輛電三輪,伸手拔掉車鑰匙,要把三輪車拖走。三輪車的女主人,忙下跪磕頭求交警高抬貴手。馬上圍了路...
    銘玥詠全閱讀 183評論 0 0
  • 聚集函數 聚集函數(aggregate function)對某些行運行的函數,計算并返回一個值。 1AVG()函數...
    ATHAS閱讀 787評論 0 50