面試被問:OOM 類型有哪些?怎么答?
面試官:OOM類型有哪些?
你:就是老年代放不下了嘛!
面試官:等消息吧!
OOM(Out Of Memory) 錯誤有多種類型,每種類型對應不同的內存區域或觸發場景。以下是常見的 OOM 類型及其產生原因:
1. java.lang.OutOfMemoryError: Java heap space
觸發原因:堆內存(存放對象實例)不足,無法分配新對象。
典型場景:
- 內存泄漏:對象被無意長期引用(如靜態集合、未關閉的資源),無法被 GC 回收。
- 堆大小不足:JVM 堆參數(-Xmx)設置過小,或程序需要處理的數據量超出預期。
- 大對象分配:一次性申請超大對象(如大數組)。
示例:
// 不斷向集合中添加對象導致堆溢出
List<Object> list = new ArrayList<>();
while (true) {
list.add(new Object());
}
解決方案:
- 檢查內存泄漏(使用 jmap + MAT 分析堆轉儲)。
- 調整堆大小(-Xmx 和 -Xms)。
- 優化代碼邏輯,減少對象生命周期。
2. java.lang.OutOfMemoryError: Metaspace(Java 8+)或 PermGen space(Java 7-)
觸發原因:元空間(Metaspace)或永久代(PermGen)內存不足,用于存儲類元數據、方法信息等。
典型場景:
- 動態生成大量類(如使用 CGLib、反射、動態代理)。
- 類加載器未正確釋放(如頻繁部署的 Web 應用導致舊類未卸載)。
示例:
// 使用 CGLib 動態生成大量代理類
Enhancer enhancer = new Enhancer();
while (true) {
enhancer.setSuperclass(OOM.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> proxy.invokeSuper(obj, args)));
enhancer.create();
}
解決方案:
- 調整元空間大小(-XX:MaxMetaspaceSize)。
- 檢查類加載器泄漏或動態類生成邏輯。
3. java.lang.OutOfMemoryError: Direct buffer memory
觸發原因:直接內存(Direct Memory,通過 ByteBuffer.allocateDirect() 分配)耗盡。
典型場景:
- 頻繁申請直接內存但未及時釋放(需依賴 System.gc() 或 Cleaner 機制)。
- JVM 直接內存參數(-XX:MaxDirectMemorySize)設置過小。
示例:
// 不斷申請直接內存
List<ByteBuffer> buffers = new ArrayList<>();
while (true) {
buffers.add(ByteBuffer.allocateDirect(1024 * 1024)); // 1MB
}
解決方案:
- 檢查直接內存使用代碼,確保及時釋放。
- 調整 -XX:MaxDirectMemorySize。
4. java.lang.OutOfMemoryError: Unable to create new native thread
觸發原因:操作系統限制線程數量,無法創建新線程。
典型場景:
- 線程數超過系統限制(如 Linux 的 ulimit -u)。
- 每個線程的棧內存(-Xss)設置過大,導致總內存占用超出。
示例:
// 無限創建線程
while (true) {
new Thread(() -> {
try { Thread.sleep(1000000); } catch (InterruptedException e) {}
}).start();
}
解決方案:
- 減少線程數(使用線程池)。
- 調整 -Xss 減小線程棧大小。
- 修改系統線程數限制。
5. java.lang.OutOfMemoryError: Requested array size exceeds VM limit
- 觸發原因:嘗試分配超過 JVM 限制的數組(通常接近 Integer.MAX_VALUE)。
- 典型場景:錯誤計算數組長度,如 new int[Integer.MAX_VALUE]。
解決方案:檢查數組長度計算邏輯,使用合理的數據結構。
6. java.lang.OutOfMemoryError: GC Overhead limit exceeded
觸發原因:GC 頻繁執行但回收效率極低(如 98% 時間用于 GC,僅回收 2% 內存)。
典型場景:堆內存幾乎被占滿,且存在大量無法回收的對象(內存泄漏)。
解決方案:檢查內存泄漏或優化 GC 策略(如調整堆大小、更換垃圾回收器)。
7. java.lang.OutOfMemoryError: CodeCache(JIT 編譯代碼緩存溢出)
觸發原因:JIT 編譯器生成的本地代碼占滿代碼緩存區。
典型場景:高頻動態編譯大量方法(如復雜的熱點代碼)。
解決方案:
- 調整代碼緩存大小(-XX:ReservedCodeCacheSize)。
- 關閉分層編譯(-XX:-TieredCompilation)。
總結
OOM 的根本原因是 JVM 內存區域不足 或 資源耗盡,需結合錯誤類型分析具體內存區域(堆、元空間、直接內存等)。
排查時可通過以下步驟:
- 確定 OOM 類型(通過錯誤日志)。
- 使用工具分析(如 jstat、jmap、VisualVM、MAT)。
- 調整 JVM 參數或優化代碼邏輯。