天天看點

線上OOM:GC Overhead Limit Exceeded異常排查(二)

一、Java的記憶體模型

《Java虛拟機規範》規定虛拟機包括以下幾個運作時資料區:

  1. 方法區 - Method Area
  2. 虛拟機棧 - VM Stack
  3. 本地方法棧 - Native Method Stack
  4. 堆 - Heap
  5. 程式計數器 - Program Counter Register
線上OOM:GC Overhead Limit Exceeded異常排查(二)

在不同的​

​JVM​

​運作時實作中可能存在差異

常見的​

​JVM​

​運作時有:

  1. OracleJDK
  2. OpenJDK
  3. IBM J9
  4. JRocket
  5. Alibaba Dragonwell
  6. Tencent Kona
  7. Graalvm

比如​

​OracleJDK8​

​的方法區區叫元空間,具體不再展開...

不考慮差異性,本文主旨是線上​

​OracleJDK8​

​說明記憶體溢出出現的場景,排查方式和解決方案

二、記憶體溢出類型

  1. StackOverflowError
  2. OutOfMemoryError

​StackOverflowError​

​​當線程棧深度超過設定的最大深度,則由虛拟機抛出 ​

​OutOfMemoryError​

​當無法通過JVM申請到記憶體時,則由虛拟機抛出

除了程式計數器外,棧、堆、非堆(直接記憶體)都可以抛出​

​OOM​

常見的異常錯誤及通用解:

  1. java.lang.StackOverflowError : Thread Stack space

原因:當線程棧深度超過設定的最大深度,則由虛拟機抛出

解決:

1)使用循環替換遞歸

2)使用排程器MapReduce思想來實作分之歸并

  1. java.lang.OutOfMemoryError: Java heap space

原因:目前線程無法從JVM申請堆記憶體空間

解決:

1)具體問題具體分析,當定位到是大對象時,需要優化代碼為小對象

2)當定位到是堆記憶體空間太小,修改VM配置。這裡需評估并發量,經過壓測和優化後得出

3)優化代碼,減少處理耗時,如果是高耗時操作,可以在對象不使用後,去掉局部變量對大對象的引用

4)當定位到是記憶體洩露,則需要修改洩露代碼(通常在引用第三方包出現)

  1. java.lang.OutOfMemoryError: Requested array size exceeds VM limit

原因:申請數組大小超過JVM允許值,不同JVM版本存在差異

解決:

1) 修改申請數組小大

  1. java.lang.OutOfMemoryError: GC Overhead Limit Exceeded

原因:當 GC 為釋放很小空間占用大量時間時抛出,通常會伴随着CPU100%異常,線上系統嚴重卡頓

解決:跟​

​Java heap space​

​處理方法一樣

  1. java.lang.OutOfMemoryError: Metaspace

原因:目前線程無法從JVM申請元空間記憶體,JDK8之後原永久代中的類中繼資料遷移到元空間(堆 -> 直接記憶體),常量池繼續在堆中

解決:

1)當定位到是元空間設定太小,修改配置

2)當定位到是記憶體洩露,則修改代碼(通常在引用第三方包出現)

  1. java.lang.OutOfMemoryError: Direct buffer memory

原因:一般是工具為了做零拷貝,直接将資料存儲在直接記憶體中,減少使用者态和核心态記憶體拷貝,當未做記憶體回收時,空間不釋放,抛出異常

解決:

1)定位到直接記憶體設定太小,修改配置

  1. VisualVM
  2. Eclipse MAT(Memory Analyzer Tool)
  3. Alibab Arthas