一.Java OOM介紹
OOM 全稱“Out Of Memory”,即“內存用完了”,來源於Java.lang包下的一個類:OutOfMemoryError :
- OOM屬於error。異常Exception和錯誤Error的區別是:異常能被程序本身可以處理,錯誤是無法處理。
- 官方解釋:Thrown when the Java Virtual Machine cannot allocate an object because it is out of memory, and no more memory could be made available by the garbage collector
- 這個錯誤會在JVM無法爲一個對象分配足夠的內存,並且GC收集器無法回收空間時拋出
二.常見異常信息
- java.lang.StackOverflowError
棧空間溢出 ,遞歸調用卡死,注意這種Error不屬於OOM,二者是並列的
實例代碼:方法調用自身,不設置返回條件,棧逐漸加深最後報錯
public static void main(String[] args) {
stackoverflow();
}
private static void stackoverflow() {
stackoverflow();
}
- java.lang.OutOfMemoryError:Java heap space
堆內存溢出 , 對象過大
實例代碼:
public static void main(String[] args) {
String str = "test";
while (true){
str += str + new Random().nextInt(11111111);
str.intern();
}
}
注意要設置JVM參數,減小堆空間heap space的容量:-Xmx10m -Xms10m
- java.lang.OutOfMemoryError:GC overhead limit exceeded
GC回收時間過長
- 過長的定義是超過98%的時間用來做GC並且回收了而不倒2%的堆內存
- 連續多次GC,都回收了不到2%的極端情況下才會拋出
- 如果不拋出,那就是GC清理的一點內存很快會被再次填滿,迫使GC再次執行,這樣就惡性循環,cpu使用率一直是100%,二GC卻沒有任何成果
實例代碼:
int i = 0;
List<String> list = new ArrayList<>();
try{
while(true){
list.add(String.valueOf(++i).intern());
}
}catch(Throwable e){
System.out.println("********");
e.printStackTrace();
throw e;
}
需設置參數 : -MaxDirectMemorySize=5m
- java.lang.OutOfMemoryError:Direct buffer memory
執行內存掛了
- 寫NIO程序經常使用ByteBuffer來讀取或寫入數據,這是一種基於通道(Channel)與緩存區(Buffer)的I/O方式,它可以使用Native函數庫直接分配堆外內存,然後通過一個存儲在java堆裏面的DirectByteBuffer對象作爲這塊內存的引用進行操作,這樣能在一些場景中顯著提高性能,因爲避免了在java堆和native堆中來回複製數據
- ByteBuffer.allocate(capability) 第一種方式是分配JVM堆內存,屬於GC管轄,由於需要拷貝所以速度較慢
- ByteBuffer.alloctedDirect(capability)分配os本地內存,不屬於GC管轄,不需要拷貝,速度較快
但如果不斷分配本地內存,堆內存很少使用,那麼jvm就不需要執行GC,DirectByteBuffer對象們就不會被回收,這時候堆內存充足,但本地內存可能已經使用光了,再次嘗試分配本地內存救護i出現oom,程序崩潰
- java.lang.OutOfMemoryError:unable to create new native thread
好案例
- 應用創建了太多線程,一個應用進程創建了多個線程,超過系統承載極限
- 你的服務器並不允許你的應用程序創建這麼多線程,linux系統默認允許單個進程可以創建的線程數是1024,超過這個數量,就會報錯
解決辦法
- 降低應用程序創建線程的數量,分析應用給是否針對需要這麼多線程,如果不是,減到最低
- 修改linux服務器配置
- java.lang.OutOfMemoryError:Metaspace
元空間主要存放了虛擬機加載的類的信息、常量池、靜態變量、即時編譯後的代碼