深入理解Java虛擬機器筆記1: OOM實戰

zhuyiquan90發表於2018-06-29

透過程式碼模擬Java虛擬機器規範中描述的各個執行時區域記憶體溢位的場景。

首先,虛擬機器啟動引數配置如下:

-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
		1	

輸出:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:2245)
    at java.util.Arrays.copyOf(Arrays.java:2219)
    at java.util.ArrayList.grow(ArrayList.java:213)
    at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:187)
    at java.util.ArrayList.add(ArrayList.java:411)
    at HeapOOM.main(HeapOOM.java:15)
Heap
 def new generation   total 9216K, used 8920K [0x32570000, 0x32f70000, 0x32f70000)
  eden space 8192K, 100% used [0x32570000, 0x32d70000, 0x32d70000)
  from space 1024K, 71% used [0x32d70000, 0x32e26040, 0x32e70000)
  to   space 1024K, 0% used [0x32e70000, 0x32e70000, 0x32f70000)
 tenured generation   total 10240K, used 5693K [0x32f70000, 0x33970000, 0x33970000)
   the space 10240K, 55% used [0x32f70000, 0x334ff7f8, 0x334ff800, 0x33970000)
 compacting perm gen  total 12288K, used 135K [0x33970000, 0x34570000, 0x37970000)
   the space 12288K, 1% used [0x33970000, 0x33991dd8, 0x33991e00, 0x34570000)
    ro space 10240K, 45% used [0x37970000, 0x37df1888, 0x37df1a00, 0x38370000)
    rw space 12288K, 54% used [0x38370000, 0x389f04f8, 0x389f0600, 0x38f70000)
		1	
		2	
		3	
		4	
		5	
		6	
		7	
		8	
		9	
		10	
		11	
		12	
		13	
		14	
		15	
		16	
		17	
		18	

深入理解Java虛擬機器筆記1: OOM實戰 2. 虛擬機器棧和本地方法棧溢位

    深入理解Java虛擬機器筆記1: OOM實戰 2.1 StackOverflowError

    虛擬機器丟擲StackOverflowError異常,輸出:

Exception in thread "main" java.lang.StackOverflowError
    at JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:7)
    at JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:8) ...
		1	
		2	
		3	
		4	

需要注意,為每個執行緒的棧分配的記憶體越大,反而越容易產生棧記憶體溢位異常。  
這個不難理解,每個執行緒分配到棧容量越大,可以建立的執行緒數量自然就越少,建立執行緒時就越容易把剩下的記憶體耗盡。  
因此,可以透過 “減少記憶體” 的手段來解決棧記憶體溢位問題。
/**
 * 
 * 
 * 功能描述: 棧OutOfMemoryError 
 * VM Args:-Xss2M 調大單執行緒可使用棧空間大小
 * @author zhuyiquan90
 * @created 2017-9-1 上午11:20:06
 * @version 1.0.0
 * @date 2017-9-1 上午11:20:06
 */ public class JavaVMStackOOM { private void dontStop() { while (true) {
        }
    } public void stackLeakByThread() { while (true) {
            Thread thread = new Thread(new Runnable() { @Override public void run() {
                    dontStop();
                }
            });
            thread.start();
        }
    } public static void main(String[] args) {
        JavaVMStackOOM oom = new JavaVMStackOOM();
        oom.stackLeakByThread();
    }
}
		1	
		2	
		3	
		4	
		5	
		6	
		7	
		8	
		9	
		10	
		11	
		12	
		13	
		14	
		15	
		16	
		17	
		18	
		19	
		20	
		21	
		22	
		23	
		24	
		25	
		26	
		27	
		28	
		29	
		30	
		31	
		32	
		33	
		34	
		35	
		36	
		37	

深入理解Java虛擬機器筆記1: OOM實戰 3. 方法區和執行時常量池溢位

JDK 1.7開始逐步“去永久代化”。在JDK 1.6及之前的版本,由於常量池分配在永久代,可以透過-XX:PermSize和-XX:MaxPermSize限制方法區大小,從而間接限制常量池容量。

import java.util.ArrayList; import java.util.List; /**
 * 
 * 
 * 功能描述: 執行時常量池記憶體溢位
 * VM Args: -XX:PermSize=10M -XX:MaxPermSize=10M
 * @author 作者 zhuyiquan90
 * @created 2017-9-1 上午11:50:48
 * @version 1.0.0
 * @date 2017-9-1 上午11:50:48
 */ public class RuntimeConstantPoolOOM { public static void main(String[] args) { // 使用List保持常量池引用,避免Full GC回收常量池行為 List<String> list = new ArrayList<String>(); // 10MB的PermSize在integer範圍內足夠產生OOM int i =0; while(true) {
            list.add(String.valueOf(i++).intern());
        }
    }
}
		1	
		2	
		3	
		4	
		5	
		6	
		7	
		8	
		9	
		10	
		11	
		12	
		13	
		14	
		15	
		16	
		17	
		18	
		19	
		20	
		21	
		22	
		23	
		24	
		25	
		26	

在JDK 1.7及以上,while迴圈將一直進行下去。

方法區用於存放Class的相關資訊,如類名、訪問修飾符、常量池、欄位描述、方法描述等。對於這些區域的測試,基本的思想是執行時產生大量的類去填滿方法去,直到溢位。
package com.suning; 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=10M -XX:MaxPermSize=10M
 * @author 作者 zhuyiquan90
 * @created 2017-9-1 下午3:31:27
 * @version 1.0.0
 * @date 2017-9-1 下午3:31:27
 */ 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 { // TODO Auto-generated method stub return proxy.invokeSuper(obj, args);
                }
            });
            enhancer.create();
        }
    } static class OOMObject {
    }
}
		1	
		2	
		3	
		4	
		5	
		6	
		7	
		8	
		9	
		10	
		11	
		12	
		13	
		14	
		15	
		16	
		17	
		18	
		19	
		20	
		21	
		22	
		23	
		24	
		25	
		26	
		27	
		28	
		29	
		30	
		31	
		32	
		33	
		34	
		35	
		36	
		37	
		38	
		39	
		40	
		41	
		42	
		43	

深入理解Java虛擬機器筆記1: OOM實戰 4. 本機直接記憶體溢位

執行結果:

Exception in thread "main" java.lang.OutOfMemoryError at sun.misc.Unsafe.allocateMemory(Native Method)
    at DirectMemoryOOM.main(DirectMemoryOOM.java:14)
		1	
		2	
		3	

深入理解Java虛擬機器筆記1: OOM實戰 小結


  • Java堆溢位

  • 虛擬機器棧和本地方法棧溢位

  • 方法區和執行時常量池溢位

  • 本機直接記憶體溢位

  • 來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31541037/viewspace-2157023/,如需轉載,請註明出處,否則將追究法律責任。

    相關文章