美文网首页
《深入理解JAVA虚拟机》阅读笔记3-实战OOM异常

《深入理解JAVA虚拟机》阅读笔记3-实战OOM异常

作者: Damo很努力 | 来源:发表于2019-07-26 08:48 被阅读0次

    除了程序计数器以外,虚拟机内存的其它几个运行时区域都有发生OutOfMemoryError异常的可能


    一、Java堆溢出

      java堆用于存储对象实例,只要不断的创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,那么在对象数量到达最大堆的容量限制后就会产生内存溢出异常!

    • VM arguments
      设置Java堆的大小20MB,将堆的最小值-Xms参数和最大值-Xmx参数设置为一样,避免堆自动扩展。通过设置参数-XX:+HeapDumpOnOutOfMemoryError可以让虚拟机在出现内存溢出异常时Dump出当前的内存堆转储快照以便事后进行分析

    • 根据Java内存区域可知,通过new指令创建的对象是放到Java堆中的。下面这段代码的功能就是一直在创建OOMObject对象,当OOMObject对象占用大小超出设置的20M时,就会报出OutOfMemoryError异常,并提示发生异常的区域为"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
    

    二、虚拟机栈溢出

      由于在HotSport虚拟机中并不区分虚拟机栈和本地方法栈,因此,对于HotSpot来说,虽然-Xoss 参数(设置本地方法栈大小)存在,但实际上是无效的,栈容量只由-Xss参数设定。关于虚拟机栈和本地方法栈,在虚拟机规范中描述了两种异常:

    1. 如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError
      异常。
    2. 如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。
    /**
     * 虚拟机栈溢出.
     *
     * 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
    

    三、方法区和运行时常量池异常

      String.intern()的作用:如果字符串常量池中已经包含一个等于此String对象的字符串,则返回常量池中这个字符串的String对象;否则,将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用。下面这段代码一直在往常量池中添加字符串,直到超出常量池的容量。

    /**
     * 方法区和运行时常量池异常.
     *
     * 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及之前的版本中,常量池分配在永久代内,可以通过-XX:PermSize和 -XX:MaxPermSize限制永久代的大小,当常量池超出容量时,会抛出异常Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
    由于JDK 1.7的HotSpot中,已经把原本放在永久代中的字符串常量池移除,因此使用JDK 1.7运行这段代码时不会抛出异常,while循环将一直进行下去


    四、本机直接内存溢出

    • 通过Unsafe对象的allocateMemory方法申请直接内存,当超出DirectMemory容量时,抛出异常java.lang.OutOfMemoryError
    /**
     * 本机直接内存溢出.
     *
     * 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)
    

    相关文章

      网友评论

          本文标题:《深入理解JAVA虚拟机》阅读笔记3-实战OOM异常

          本文链接:https://www.haomeiwen.com/subject/rhpfrctx.html