深入理解JVM之OOM
做Java程式開發,難免會遇到OutOfMemory,導致的原因也是不盡相同,下面我們來捋一捋OOM出現的場景。
一,堆空間不足,這是最容易,也是最常見的OOM。瞭解Java記憶體結構的同學應該知道,Java裡面建立的物件大部分都是位於堆空間的,當建立的物件太多,而堆空間不足時,很容易丟擲OOM,如下:
執行結果:
二,直接分配記憶體溢位,Java提供了一些可以直接操作記憶體和執行緒的低層次操作(native)-sun.misc.Unsafe,其被JDK廣泛用於自己的包中,如java.nio和java.util.concurrent。
但是絲毫不建議在生產環境中使用這個Unsafe,從名字就可以看出,這個API十分不安全、不輕便、而且不穩定。
執行結果:
三,方法區溢位,方法區主要存放類的資訊、靜態變數、Field、Method資訊等,當不停地有類動態建立並載入時,方法區也能產生OOM。
執行結果:
四,常量池溢位,常量池屬於方法區,存放一些常量值(如static final),還有一些文字形式出現的符號引用,如:類和介面的全限定名、欄位的名稱和描述符。
執行結果:
五,Stack溢位,JVM方法棧為執行緒私有,當方法執行時會被建立,當方法執行完畢,其對應的棧幀所佔用的記憶體也會被自動釋放。
當方法棧的深度不足時,會丟擲StackOverflowError,不過只要不出現無窮遞迴,棧的深度不會太大。
執行結果:
補充:
一,堆空間不足,這是最容易,也是最常見的OOM。瞭解Java記憶體結構的同學應該知道,Java裡面建立的物件大部分都是位於堆空間的,當建立的物件太多,而堆空間不足時,很容易丟擲OOM,如下:
點選(此處)摺疊或開啟
-
import java.util.ArrayList;
-
import java.util.List;
-
-
/*VM args:-Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError
-
*
-
* -Xms:堆的最小值;-Xmx:堆的最大值
-
*
-
* */
-
public class HeapOOM {
-
-
static class OOMObject {
-
}
-
-
public static void main(String[] args) {
-
List<OOMObject> list = new ArrayList<OOMObject>();
-
while (true) {
-
list.add(new OOMObject());
-
}
-
}
- }
點選(此處)摺疊或開啟
-
Exception in thread \"main\" java.lang.OutOfMemoryError: Java heap space
-
at java.util.Arrays.copyOf(Arrays.java:2760)
-
at java.util.Arrays.copyOf(Arrays.java:2734)
-
at java.util.ArrayList.ensureCapacity(ArrayList.java:167)
-
at java.util.ArrayList.add(ArrayList.java:351)
- at test.java.VM.OOM.HeapOOM.main(HeapOOM.java:19)
二,直接分配記憶體溢位,Java提供了一些可以直接操作記憶體和執行緒的低層次操作(native)-sun.misc.Unsafe,其被JDK廣泛用於自己的包中,如java.nio和java.util.concurrent。
但是絲毫不建議在生產環境中使用這個Unsafe,從名字就可以看出,這個API十分不安全、不輕便、而且不穩定。
點選(此處)摺疊或開啟
-
import java.lang.reflect.Field;
-
-
import sun.misc.Unsafe;
-
-
/*VM args:-Xmx10m -XX:MaxDirectMemorySize=5m
-
*
-
* */
-
public class DirectMemoryOOM {
-
private static final int _1MB = 1024 * 1024;
-
-
public static void main(String[] args) throws Exception {
-
Field unsafeField = Unsafe.class.getDeclaredField(\"theUnsafe\");
-
unsafeField.setAccessible(true);
-
Unsafe unsafe = (Unsafe) unsafeField.get(null);
-
-
while (true) {
-
unsafe.allocateMemory(_1MB);
-
}
-
-
}
-
- }
點選(此處)摺疊或開啟
-
Exception in thread \"main\" java.lang.OutOfMemoryError
-
at sun.misc.Unsafe.allocateMemory(Native Method)
- at test.java.VM.OOM.DirectMemoryOOM.main(DirectMemoryOOM.java:19)
三,方法區溢位,方法區主要存放類的資訊、靜態變數、Field、Method資訊等,當不停地有類動態建立並載入時,方法區也能產生OOM。
點選(此處)摺疊或開啟
-
import java.lang.reflect.Method;
-
-
import net.sf.cglib.proxy.Enhancer;
-
import net.sf.cglib.proxy.MethodInterceptor;
-
import net.sf.cglib.proxy.MethodProxy;
-
-
/*VM args: -XX:PermSize=5m -XX:MaxPermSize=5m
-
*
-
* */
-
public class JavaMethodAreaOOM {
-
public static void main(String[] args) {
-
while (true) {
-
Enhancer enhancer = new Enhancer();
-
enhancer.setSuperclass(OOMObject.class);
-
enhancer.setUseCache(false);
-
enhancer.setCallback(new MethodInterceptor() {
-
@Override
-
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
-
//return method.invoke(obj, args);
-
return proxy.invokeSuper(obj, args);
-
}
-
});
-
OOMObject object = (OOMObject) enhancer.create();
-
}
-
-
}
-
-
static class OOMObject {
-
}
- }
點選(此處)摺疊或開啟
- Exception in thread \"main\" java.lang.OutOfMemoryError: PermGen space
四,常量池溢位,常量池屬於方法區,存放一些常量值(如static final),還有一些文字形式出現的符號引用,如:類和介面的全限定名、欄位的名稱和描述符。
點選(此處)摺疊或開啟
-
import java.util.ArrayList;
-
import java.util.List;
-
-
/*VM rags:-XX:PermSize=5m -XX:MaxPermSize=5m
-
*
-
*
-
* */
-
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());//將String物件加入常量池
-
}
-
}
- }
點選(此處)摺疊或開啟
-
Exception in thread \"main\" java.lang.OutOfMemoryError: PermGen space
-
at java.lang.String.intern(Native Method)
-
at test.java.VM.OOM.RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java:17)
-
Exception in thread \"Reference Handler\" java.lang.OutOfMemoryError: PermGen space
- at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:123)
五,Stack溢位,JVM方法棧為執行緒私有,當方法執行時會被建立,當方法執行完畢,其對應的棧幀所佔用的記憶體也會被自動釋放。
當方法棧的深度不足時,會丟擲StackOverflowError,不過只要不出現無窮遞迴,棧的深度不會太大。
點選(此處)摺疊或開啟
-
/*VM args:-Xss128k
-
*
-
* 在單執行緒下,無論是由於棧幀太大,還是虛擬機器容量太小,
-
* 當記憶體無法分配的時候,虛擬機器丟擲的都是StackOverflowError
-
* 如果測試不限於單執行緒,透過不斷建立執行緒的方式倒是可以產生記憶體溢位異常(詳見JavaVMStackOF2)
-
* */
-
public class JavaVMStackOF {
-
private int stackLength = 1;
-
-
public void stackLeak() {
-
stackLength++;//其實,即使沒有運算元,也會throw StackOverflowError
-
stackLeak();
-
}
-
-
public static void main(String[] args) throws Throwable {
-
JavaVMStackOF oom = new JavaVMStackOF();
-
try {
-
oom.stackLeak();
-
} catch (Throwable e) {
-
System.out.println(\"stack length:\" + oom.stackLength);
-
throw e;
-
}
-
}
- }
點選(此處)摺疊或開啟
-
stack length:1007Exception in thread \"main\" java.lang.StackOverflowError
-
-
at test.java.VM.OOM.JavaVMStackOF.stackLeak(JavaVMStackOF.java:13)
- at test.java.VM.OOM.JavaVMStackOF.stackLeak(JavaVMStackOF.java:14)
補充:
點選(此處)摺疊或開啟
-
/*VM args:-Xss10m
-
*
-
* 透過不斷建立新執行緒達到OutOfMemoryError
-
* 實體記憶體-Xmx(最大堆容量)-MaxPermSize(最大方法區容量)=虛擬機器棧+本地方法棧,程式計數器消耗記憶體較小,可以忽略。
-
* -Xss10m,分配給每個執行緒的記憶體。所以-Xss越大,越容易出現OutOfMemoryError(可建立的執行緒越少)。
-
*
-
* 棧深度在虛擬機器預設情況下,達到1000~2000完全沒問題,對於正常的方法呼叫(包括遞迴),這個深度完全夠用了。
-
* 但是,如果是建立過多執行緒導致 的記憶體溢位,在不能減少執行緒數或者更換64位虛擬機器的情況下,就只能透過減少最大堆容量和減少棧容量來換取更多的執行緒。
-
* 如果沒有這方面的經驗,這種透過“減少記憶體”的手段來解決記憶體溢位的方式會比較難以想到!
- * */
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/28912557/viewspace-1455299/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 深入理解 JVM 之 JVM 記憶體結構JVM記憶體
- 深入理解JVM(1)之--JVM記憶體模型JVM記憶體模型
- JVM讀書筆記之OOMJVM筆記OOM
- 深入理解 JVMJVM
- 深入理解JVMJVM
- 深入理解 JVM 之 垃圾回收機制JVM
- 深入理解 JVM 之 垃圾收集器JVM
- 深入理解JVM——物件JVM物件
- [譯]深入理解JVM Understanding JVM InternalsJVM
- 深入理解JVM虛擬機器6:深入理解JVM類載入機制JVM虛擬機
- 深入理解JVM(一)JVM記憶體模型JVM記憶體模型
- 深入理解JVM(一)——JVM記憶體模型JVM記憶體模型
- 深入理解JVM效能調優JVM
- 深入理解JVM(③)——之HotSpot虛擬機器物件探祕JVMHotSpot虛擬機物件
- 《深入理解JVM》10-垃圾回收JVM
- 深入理解JVM(③)Java的鎖優化JVMJava優化
- 深入理解JVM(③)Java的模組化JVMJava
- JVM 原始碼分析(三):深入理解 CASJVM原始碼
- JVM相關 - 深入理解 System.gc()JVMGC
- 深入理解Java虛擬機器筆記1: OOM實戰Java虛擬機筆記OOM
- 深入探究JVM之垃圾回收器JVM
- 深入理解JVM——(二)搞定JVM垃圾回收就是這麼簡單JVM
- 深入理解Java虛擬機器之JVM記憶體佈局篇Java虛擬機JVM記憶體
- 深入理解JVM——(四)類載入機制JVM
- 深入理解JVM位元組碼執行引擎JVM
- 深入理解JVM,7種垃圾收集器JVM
- 深入理解JVM(③)再談執行緒安全JVM執行緒
- 總結《深入理解JVM》 G1 篇JVM
- 深入理解JVM(③)判斷物件是否還健在?JVM物件
- 深入理解JVM(三)——垃圾收集策略詳解JVM
- 深入理解JVM:效能分析與監控工具JVM
- JVM 原始碼分析(四):深入理解 park / unparkJVM原始碼
- 【深入理解JVM】方法區 永久代 元空間JVM
- 深入理解JVM(四)類載入的時機JVM
- 深入理解jvm記憶體模型以及gc原理JVM記憶體模型GC
- 深入理解JVM(③)各種垃圾收集演算法JVM演算法
- 深入理解PHP之strposPHP
- 深入理解Javascript之PromiseJavaScriptPromise
- 深入理解PHP之foreachPHP