Java OOM認知
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!