JVM解讀(四):JVM記憶體溢位異常分析
JVM全稱是java Virtual Machine(java虛擬機器),JVM遮蔽了與各個計算機平臺相關的軟體和硬體差異。
在接下來的日子裡,通過寫部落格的形式學習JVM,讓自己更懂得Java!
本系列文章是對《深入分析javaweb技術內幕》和《深入理解java虛擬機器》的總結,歡迎大家一起吐槽,一起進步。
《JVM解讀》第一篇:JVM體系結構
《JVM解讀》第二篇:JVM類載入器ClassLoader
《JVM解讀》第三篇:JVM記憶體區域
《JVM解讀》第四篇:JVM記憶體溢位異常分析
《JVM解讀》第五篇:JVM垃圾收集
在實際的開發中我們可能會遇到各種各樣的記憶體溢位(OutOfMemoryError)問題,我在開發的時候就遇到過這樣的情況。當時是報的這樣異常
Caused by:java.lang.OutOfMemoryError:PermGen Space
at java.lang.ClassLoader……
造成這個原因的是我們的tomcat下方了好幾個web程式,而且每個程式都有大量的Spring ,hibernate的jar包,並且這幾個程式的jar基本上都是一樣的。這就造成了重複載入的情況,直接導致我的永久區溢位。後來把公共的jar提出來放在一個share資料夾下,這樣就可以了。而且Spring,Hibernate,在對類進行增強時,都會使用到CGLib這類位元組碼技術,增強的類越多,就需要越大的方法區來保證動態生成的class可以載入如記憶體。
java堆溢位
java堆用於儲存物件,所有不斷的建立物件並且在GC Roots到物件之間有可達路徑避免垃圾回收清除這些物件,在物件數量達到最大堆的容量限制後就會產生堆溢位
public class HeapOOM {
static class OOMObject{}
public static void main(String[] args) {
List<OOMObject> list=new ArrayList<HeapOOM.OOMObject>();
while(true)
list.add(new OOMObject());
}
虛擬機器棧和本地方法棧溢位
棧容量的設定由引數-Xss引數設定。java虛擬機器規範中描述的異常
- 如果執行緒請求的棧深度大於虛擬機器所執行的最大深度,將丟擲StackOverflowError
- 如果虛擬機器在擴充套件時無法申請到足夠的記憶體空間,則丟擲OutOfMemoryError異常
public class JavaVMStackOF {
private int stackLength=1;
public void stackLeak()
{
stackLength++;
stackLeak();
}
public static void main(String[] args) throws Throwable {
JavaVMStackOF jvof=new JavaVMStackOF();
try {
jvof.stackLeak();
} catch (Throwable e) {
System.out.println("stack length:"+jvof.stackLength);
throw e;
}
}
}
實驗表明:在當個執行緒下,無論由於棧幀太大還是虛擬機器棧容量太小,當記憶體無法分配時,虛擬機器丟擲的都是StackOverflowErroe異常
方法區和執行時常量池溢位
執行時 常量池是方法區的一部分。我們通過一個String.intern()方法來測試。String.intern()是一個本地方法,如果字串常量池中已經包含一個等於此String物件,則返回代表池中這個字串的String物件;否則將此String物件包含的字串新增到常量池中,並且返回該String物件的引用。
public class RuntimeConstantPoolOOM {
public static void main(String[] args) {
//使用list保持對常量池引用,避免Full GC回收常量池
List<String> list=new ArrayList<String>();
int i=0;
while(true)
list.add(String.valueOf(i++).intern());
}
這裡我一開始是用jre7的,結果沒反應,改為jre6才有產生這樣的情況。
JVM上的動態語言通常會持續建立類來實現語言的動態性,也會經常造成PermGen space異常
直接記憶體溢位
DirectMemory容量可通過-XX:MaxDirectMemorySize指定,如果不指定,預設與java堆的最大值-Xmx一樣。
public class DirectMemoryOOM {
private static final int _1MB=1024*1024;
public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
Field unsafeField=Unsafe.class.getDeclaredFields()[0];
unsafeField.setAccessible(true);
Unsafe unsafe=(Unsafe) unsafeField.get(null);
while(true)
unsafe.allocateMemory(_1MB);
}
由DirectMemory導致的記憶體溢位,一個很明顯的特徵是在Heap Dump檔案中不會看見明顯的異常。如果發現OOM情況後Dump檔案很小,而程式中又使用了NIO,可以檢查是不是直接記憶體溢位。