《深入理解JAVA虛擬機(jī)》閱讀筆記3-實戰(zhàn)OOM異常

除了程序計數(shù)器以外,虛擬機(jī)內(nèi)存的其它幾個運行時區(qū)域都有發(fā)生OutOfMemoryError異常的可能


一、Java堆溢出

??java堆用于存儲對象實例,只要不斷的創(chuàng)建對象,并且保證GC Roots到對象之間有可達(dá)路徑來避免垃圾回收機(jī)制清除這些對象,那么在對象數(shù)量到達(dá)最大堆的容量限制后就會產(chǎn)生內(nèi)存溢出異常!

  • VM arguments
    設(shè)置Java堆的大小20MB,將堆的最小值-Xms參數(shù)和最大值-Xmx參數(shù)設(shè)置為一樣,避免堆自動擴(kuò)展。通過設(shè)置參數(shù)-XX:+HeapDumpOnOutOfMemoryError可以讓虛擬機(jī)在出現(xiàn)內(nèi)存溢出異常時Dump出當(dāng)前的內(nèi)存堆轉(zhuǎn)儲快照以便事后進(jìn)行分析

  • 根據(jù)Java內(nèi)存區(qū)域可知,通過new指令創(chuàng)建的對象是放到Java堆中的。下面這段代碼的功能就是一直在創(chuàng)建OOMObject對象,當(dāng)OOMObject對象占用大小超出設(shè)置的20M時,就會報出OutOfMemoryError異常,并提示發(fā)生異常的區(qū)域為"Java heap space"

/**
 * -Dfile.encoding=UTF-8 
 * -verbose:gc 
 * -Xms20m 
 * -Xmx20m 
 * -Xmn10M 
 * -XX:+PrintGCDetails    
 * -XX:SurvivorRatio=8
 */
public class HeapOOM {
    static class OOMObject {

    }

    public static void main(String[] agrs) {
        List<OOMObject> list = new ArrayList<OOMObject>();

        while (true) {
            list.add(new OOMObject());
        }
    }
}
  • 控制臺輸出
[GC (Allocation Failure) [PSYoungGen: 8192K->1008K(9216K)] 8192K->4849K(19456K), 0.0233638 secs] [Times: user=0.03 sys=0.01, real=0.02 secs] 
[GC (Allocation Failure) --[PSYoungGen: 9200K->9200K(9216K)] 13041K->19432K(19456K), 0.0403577 secs] [Times: user=0.05 sys=0.00, real=0.04 secs] 
[Full GC (Ergonomics) [PSYoungGen: 9200K->0K(9216K)] [ParOldGen: 10232K->10179K(10240K)] 19432K->10179K(19456K), [Metaspace: 3324K->3324K(1056768K)], 0.1432548 secs] [Times: user=0.21 sys=0.00, real=0.14 secs] 
[Full GC (Ergonomics) [PSYoungGen: 8018K->8177K(9216K)] [ParOldGen: 10179K->8068K(10240K)] 18197K->16246K(19456K), [Metaspace: 3325K->3325K(1056768K)], 0.1312606 secs] [Times: user=0.19 sys=0.01, real=0.13 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 8177K->8165K(9216K)] [ParOldGen: 8068K->8063K(10240K)] 16246K->16228K(19456K), [Metaspace: 3325K->3325K(1056768K)], 0.0686279 secs] [Times: user=0.17 sys=0.00, real=0.07 secs] 
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    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.damo.note.java.jvm.HeapOOM.main(HeapOOM.java:22)
[Full GC (Ergonomics) [PSYoungGen: 8192K->0K(9216K)] [ParOldGen: 8063K->401K(10240K)] 16255K->401K(19456K), [Metaspace: 3350K->3350K(1056768K)], 0.0051904 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
Heap
 PSYoungGen      total 9216K, used 82K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
  eden space 8192K, 1% used [0x00000007bf600000,0x00000007bf614938,0x00000007bfe00000)
  from space 1024K, 0% used [0x00000007bfe00000,0x00000007bfe00000,0x00000007bff00000)
  to   space 1024K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007c0000000)
 ParOldGen       total 10240K, used 401K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
  object space 10240K, 3% used [0x00000007bec00000,0x00000007bec644e8,0x00000007bf600000)
 Metaspace       used 3357K, capacity 4500K, committed 4864K, reserved 1056768K
  class space    used 375K, capacity 388K, committed 512K, reserved 1048576K

二、虛擬機(jī)棧溢出

??由于在HotSport虛擬機(jī)中并不區(qū)分虛擬機(jī)棧和本地方法棧,因此,對于HotSpot來說,雖然-Xoss 參數(shù)(設(shè)置本地方法棧大?。┐嬖冢珜嶋H上是無效的,棧容量只由-Xss參數(shù)設(shè)定。關(guān)于虛擬機(jī)棧和本地方法棧,在虛擬機(jī)規(guī)范中描述了兩種異常:

  1. 如果線程請求的棧深度大于虛擬機(jī)所允許的最大深度,將拋出StackOverflowError
    異常。
  2. 如果虛擬機(jī)在擴(kuò)展棧時無法申請到足夠的內(nèi)存空間,則拋出OutOfMemoryError異常。
/**
 * 虛擬機(jī)棧溢出.
 *
 * VM Args:-Xss160k
 *
 * Created by Damo on 2019-07-24 18:29.
 */
public class JavaVMStackSOF {
    private int stackLength = 1;

    public void stackLeak() {
        stackLength++;
        stackLeak();
    }

    public static void main(String[] args) throws Throwable {
        JavaVMStackSOF oom = new JavaVMStackSOF();
        try {
            oom.stackLeak();
        } catch (Throwable e) {
            System.out.println("stack length : " + oom.stackLength);
            throw e;
        }
    }

}
  • 控制臺輸出
stack length : 1201
Exception in thread "main" java.lang.StackOverflowError

三、方法區(qū)和運行時常量池異常

??String.intern()的作用:如果字符串常量池中已經(jīng)包含一個等于此String對象的字符串,則返回常量池中這個字符串的String對象;否則,將此String對象包含的字符串添加到常量池中,并且返回此String對象的引用。下面這段代碼一直在往常量池中添加字符串,直到超出常量池的容量。

/**
 * 方法區(qū)和運行時常量池異常.
 *
 * VM Args:
 *   -XX:PermSize=10M -XX:MaxPermSize=10M
 *
 * Created by Damo on 2019-07-24 19:09.
 */
public class RuntimeConstantPoolOOM {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        List<String> list = new ArrayList<String>();
        int i = 0;
        while (true) {
            list.add(String.valueOf(i++).intern());
        }
    }

}
  • 控制臺輸出

??JDK 1.6及之前的版本中,常量池分配在永久代內(nèi),可以通過-XX:PermSize和 -XX:MaxPermSize限制永久代的大小,當(dāng)常量池超出容量時,會拋出異常Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
由于JDK 1.7的HotSpot中,已經(jīng)把原本放在永久代中的字符串常量池移除,因此使用JDK 1.7運行這段代碼時不會拋出異常,while循環(huán)將一直進(jìn)行下去


四、本機(jī)直接內(nèi)存溢出

  • 通過Unsafe對象的allocateMemory方法申請直接內(nèi)存,當(dāng)超出DirectMemory容量時,拋出異常java.lang.OutOfMemoryError
/**
 * 本機(jī)直接內(nèi)存溢出.
 *
 * VM Args: -Xmx20M -XX:MaxDirectMemorySize=10M
 *
 * Created by Damo on 2019-07-24 19:15.
 */
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);
        }
    }

}
  • 控制臺輸出
Exception in thread "main" java.lang.OutOfMemoryError
        at sun.misc.Unsafe.allocateMemory(Native Method)
        at DirectMemoryOOM.main(DirectMemoryOOM.java:16)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容