Java OOM認知

語言: CN / TW / HK

highlight: atom-one-dark theme: fancy


這是我參與11月更文挑戰的第21天,活動詳情檢視:2021最後一次更文挑戰

一. StackOverflowError

1.1 bug

```java public class StackOverflowErrorDemo { public static void main(String[] args) { javaKeeper(); }

private static void javaKeeper() {
    javaKeeper();
}

} ```

JVM 虛擬機器棧是有深度的,在執行方法的時候會伴隨著入棧和出棧,上邊的方法可以看到,main 方法執行後不停的遞迴,遲早把棧撐爆了

bash Exception in thread "main" java.lang.StackOverflowError at oom.StackOverflowErrorDemo.javaKeeper(StackOverflowErrorDemo.java:15)

1.2 原因分析

  • 無限遞迴迴圈呼叫(最常見原因),要時刻注意程式碼中是否有了迴圈呼叫方法而無法退出的情況
  • 執行了大量方法,導致執行緒棧空間耗盡
  • 方法內聲明瞭海量的區域性變數
  • native 程式碼有棧上分配的邏輯,並且要求的記憶體還不小,比如 java.net.SocketInputStream.read0 會在棧上要求分配一個 64KB 的快取(64位 Linux)

1.3 解決方案

  • 修復引發無限遞迴呼叫的異常程式碼, 通過程式丟擲的異常堆疊,找出不斷重複的程式碼行,按圖索驥,修復無限遞迴 Bug
  • 排查是否存在類之間的迴圈依賴(當兩個物件相互引用,在呼叫toString方法時也會產生這個異常)
  • 通過 JVM 啟動引數 -Xss 增加執行緒棧記憶體空間, 某些正常使用場景需要執行大量方法或包含大量區域性變數,這時可以適當地提高執行緒棧空間限制

二. Java heap space

Java 堆用於儲存物件例項,我們只要不斷的建立物件,並且保證 GC Roots 到物件之間有可達路徑來避免 GC 清除這些物件,那隨著物件數量的增加,總容量觸及堆的最大容量限制後就會產生記憶體溢位異常。

Java 堆記憶體的 OOM 異常是實際應用中最常見的記憶體溢位異常。

2.1 bug

```java /* * JVM引數:-Xmx12m / public class JavaHeapSpaceDemo {

static final int SIZE = 2 * 1024 * 1024;

public static void main(String[] a) {
    int[] i = new int[SIZE];
}

} ```

程式碼試圖分配容量為 2M 的 int 陣列,如果指定啟動引數 -Xmx12m,分配記憶體就不夠用,就類似於將 XXXL 號的物件,往 S 號的 Java heap space 裡面塞。

bash Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at oom.JavaHeapSpaceDemo.main(JavaHeapSpaceDemo.java:13)

2.2 原因分析

  • 請求建立一個超大物件,通常是一個大陣列
  • 超出預期的訪問量/資料量,通常是上游系統請求流量飆升,常見於各類促銷/秒殺活動,可以結合業務流量指標排查是否有尖狀峰值
  • 過度使用終結器(Finalizer),該物件沒有立即被 GC
  • 記憶體洩漏(Memory Leak),大量物件引用沒有釋放,JVM 無法對其自動回收,常見於使用了 File 等資源沒有回收

2.3 解決方案

針對大部分情況,通常只需要通過 -Xmx 引數調高 JVM 堆記憶體空間即可。如果仍然沒有解決,可以參考以下情況做進一步處理:

  • 如果是超大物件,可以檢查其合理性,比如是否一次性查詢了資料庫全部結果,而沒有做結果數限制
  • 如果是業務峰值壓力,可以考慮新增機器資源,或者做限流降級。
  • 如果是記憶體洩漏,需要找到持有的物件,修改程式碼設計,比如關閉沒有釋放的連線

記憶體洩露和記憶體溢位

記憶體溢位(out of memory),是指程式在申請記憶體時,沒有足夠的記憶體空間供其使用,出現out of memory;比如申請了一個 Integer,但給它存了 Long 才能存下的數,那就是記憶體溢位。

記憶體洩露( memory leak),是指程式在申請記憶體後,無法釋放已申請的記憶體空間,一次記憶體洩露危害可以忽略,但記憶體洩露堆積後果很嚴重,無論多少記憶體,遲早會被佔光。

memory leak 最終會導致 out of memory!