深入理解Java虛擬機器之實戰OutOfMemoryError
堆OOM
測試程式:
/** * VM Args: -Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError * User:wangs * Date:2018/8/23 */ public class HeapOOM { public static void main(String[] args) { List<Object> list = new ArrayList<>(); while (true) { list.add(new Object()); } } } 複製程式碼
執行結果:
java.lang.OutOfMemoryError: Java heap space Dumping heap to java_pid18148.hprof ... Exception in thread "main" java.lang.OutOfMemoryError: Java heap space Heap dump file created [13355339 bytes in 0.137 secs] at java.util.Arrays.copyOf(Arrays.java:3210) at java.util.Arrays.copyOf(Arrays.java:3181) at java.util.ArrayList.grow(ArrayList.java:261) at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235) at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227) at java.util.ArrayList.add(ArrayList.java:458) at com.thinkinjava.jvm.HeapOOM.main(HeapOOM.java:18) 複製程式碼
用MAT工具開啟堆快照檔案:
“Shallow Heap”:類本身元資料的大小。“Retained Heap”:該類以及它引用的其他類所佔用空間的總和
大概的分析步驟就是這樣,關於MAT的使用,可以去多瞭解下。
棧OOM
- 執行緒請求的棧深度大於虛擬機器所允許的最大深度,將丟擲StackOverflowError。
- 如果虛擬機器在擴充套件棧時無法申請到足夠的記憶體,則丟擲OOM。
測試程式:
/** * VM Args:-Xss128k * User:wangs * Date:2018/8/27 */ public class StackOOM { private int stackLength = 1; public static void main(String[] args) { StackOOM stackOOM = new StackOOM(); try { stackOOM.test(); } catch (Throwable e) { System.out.println("stack length:" + stackOOM.stackLength); throw e; } } private void test() { stackLength++; test(); } } 複製程式碼
執行結果:
stack length:994 Exception in thread "main" java.lang.StackOverflowError at com.thinkinjava.jvm.StackOOM.test(StackOOM.java:25) at com.thinkinjava.jvm.StackOOM.test(StackOOM.java:26) at com.thinkinjava.jvm.StackOOM.test(StackOOM.java:26) at com.thinkinjava.jvm.StackOOM.test(StackOOM.java:26) at com.thinkinjava.jvm.StackOOM.test(StackOOM.java:26) at com.thinkinjava.jvm.StackOOM.test(StackOOM.java:26) at com.thinkinjava.jvm.StackOOM.test(StackOOM.java:26) at com.thinkinjava.jvm.StackOOM.test(StackOOM.java:26) at com.thinkinjava.jvm.StackOOM.test(StackOOM.java:26) at com.thinkinjava.jvm.StackOOM.test(StackOOM.java:26) at com.thinkinjava.jvm.StackOOM.test(StackOOM.java:26) at com.thinkinjava.jvm.StackOOM.test(StackOOM.java:26) at com.thinkinjava.jvm.StackOOM.test(StackOOM.java:26) at com.thinkinjava.jvm.StackOOM.test(StackOOM.java:26) 複製程式碼
上篇文章 ofollow,noindex">深入理解Java虛擬機器之棧幀 ,介紹了棧幀,我們來回顧一下:棧幀是儲存在虛擬機器棧中的,棧幀是用來儲存資料和儲存部分過程結果的資料結構,同時也被用來處理動態連結(Dynamic Linking)、方法返回值和異常分派(Dispatch Exception)。棧幀越大,棧深度越小。方法引數過多或者區域性變數過多都會使棧深度變小,如果超過-xss設定的棧記憶體容量,就會導致棧溢位。
方法區OOM
測試程式:
/* * VM Args: -XX:PermSize=10m -XX:MaxPermSize=10m */ public class RuntimeConstantPoolOOM { public static void main(String[] args) { List<String> list = new ArrayList<>(); int i = 0; while (true) { list.add(String.valueOf(i++).intern()); } } } 複製程式碼
在JDK1.6及之前版本中,由於常量池分配在永久代中(即方法區),我們可以通過-XX:PermSize和-XX:MaxPermSize限制方法區大小,從而間接限制其中常量池的容量。執行時常量池溢位: java.lang.OutOfMemoryError: PermGen space
。
JDK1.7常量池不在儲存物件,而是儲存物件的引用。修改虛擬機器引數 -Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError
,執行結果如下:
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded at java.lang.Integer.toString(Integer.java:401) at java.lang.String.valueOf(String.java:3099) at com.thinkinjava.jvm.RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java:14) 複製程式碼
異常的意思是程式花在垃圾回收上的時間太多,卻沒有什麼效果。預設的話,如果98%的時間都花在GC上並且回收了才不到2%的空間的話,虛擬機器就會拋這個異常。
JDK1.8移除了永久代,類的元資料資訊儲存在元空間。元空間並不在虛擬機器中,而是使用本地記憶體。因此,預設情況下,元空間的大小僅受本地記憶體限制,但可以通過 -XX:MetaspaceSize -XX:MaxMetaspaceSize
引數來指定元空間的大小。
測試程式:
/** * VM Args:-XX:MaxMetaspaceSize=10m * User:wangs * Date:2018/8/27 */ public class MetaspaceOOM { static ClassPool classPool = ClassPool.getDefault(); public static void main(String[] args) throws Exception { int i = 1; while (true){ classPool.makeClass("com.thinkinjava.jvm" + i++).toClass(); } } } 複製程式碼
執行結果:
Exception in thread "main" java.lang.OutOfMemoryError: Metaspace at javassist.ClassPool.toClass(ClassPool.java:1170) at javassist.ClassPool.toClass(ClassPool.java:1113) at javassist.ClassPool.toClass(ClassPool.java:1071) at javassist.CtClass.toClass(CtClass.java:1264) at com.thinkinjava.jvm.Metaspace.main(Metaspace.java:16) 複製程式碼
直接記憶體OOM
測試程式:
/** * VM Args:-Xmx20M -XX:MaxDirectMemorySize=10M * User:wangs * Date:2018/8/27 */ public class DirectMemoryOOM { private static final int _1MB = 1024 * 1024; public static void main(String[] args) throws Exception { Field unsafeField = Unsafe.class.getDeclaredFields()[0]; 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 com.thinkinjava.jvm.DirectMemoryOOM.main(DirectMemoryOOM.java:21)複製程式碼
如果讀完覺得有收穫的話,歡迎點贊、關注、加公眾號【Java線上】,查閱更多精彩歷史!!!