OutOfMemryError異常實戰
1、java堆溢位
java堆用於儲存物件例項,只要不斷建立物件,並且保證GC Roots(一直GC回收演算法)到物件之間有可達路徑來避免垃圾回收機制清除這些物件,那麼物件在物件數量到達最大堆的容量限制後就會產生記憶體溢位異常。
java堆異常是實際應用常見的記憶體溢位異常情況。當出現JAVA堆記憶體溢位情況時,異常堆疊資訊“java,lang.OutOfMemoryError” 跟著進一步提示“java heap space”。
使用記憶體分析工具對堆記憶體進行分析,次數使用的是Eclipse memory Analyzer 獨立安裝。
測試程式碼:
public class OomError {
static class OOMObject{
}
public static void main(String[] args) {
List<OOMObject> lists = new ArrayList<>();
int i = 1;
while (true){
i++ ;
if(i<1000000){
lists.add(new OOMObject());
}
System.out.println("物件初始化完成");
}
}
}
複製程式碼
記憶體洩漏分析:
由於GC Roots引用鏈的資訊,物件沒有被回收,通過分析可以準確定位記憶體洩漏程式碼的位置。
2、虛擬機器棧和本地方法棧溢位
public class OomError {
private int stackLen = 1;
public void stackLeak(){
stackLen ++ ;
stackLeak();
}
public static void main(String[] args) {
OomError oom = null;
try{
oom = new OomError();
oom.stackLeak();
}catch (Throwable e){
e.printStackTrace();
System.out.println("棧的最大深度"+oom.stackLen);
}
}
}
}catch (Throwable e){
e.printStackTrace();
System.out.println("棧的最大深度"+oom.stackLen);
}
}
}
複製程式碼
異常資訊:
java.lang.StackOverflowError
at com.haihang.exception.OomError.stackLeak(OomError.java:12)
棧的最大深度16726
複製程式碼
實驗證明:在單執行緒下記憶體無法分配時,虛擬機器都是丟擲StackOverflowError異常。
在多執行緒下,通過減少最大的堆和減少棧容量來換取更多的執行緒。
方法區和執行時常量池溢位
public class OomError {
public static void main(String[] args) {
//保持物件的引用防止GC
List<String> lists =new ArrayList<>();
int i=0;
while(true){
//intern 為本地方法,作用是先從常量池中查詢字元,如果不存在將字元放入常量池
lists.add(String.valueOf(i++).intern());
}
}
}
複製程式碼
異常資訊:
java.lang.OutOfMemoryError:Permgen space
複製程式碼
在HotSpot虛擬機器中Permgen space 屬於堆中的永久代。
圖片相同的程式碼邏輯返回的結果卻是不一樣,因為在jdk1.7以後的intern不在複製,只是在常量池中記錄物件的引用,因此intern返回的引用於StringBuilder建立的字串例項是同一個,所以相同;str2返回false的原因是“java”這個字元在執行toString之前已經出現過,不符合“首次出現”的原則,而計算機軟體是首次出現的,因此返回true。