• 深入理解JVM虚拟机(二):JDK 内存类的异常分析


    JVM数据存储

    1. 堆存储(Heap):对象存储,实际上就是JAVA的数据存储
    2. 方法堆栈(Method Stack):存储方法调用的关系。
    3. 永久代(Perm):在JDK1.6及之前,常量数据存储于此区域

    异常示例

     

    堆存储异常

    package com.wlzjdm.jvm.learning;
    
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * VM args : -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
     * 模拟内存溢出情况,抛出:java.lang.OutOfMemoryError: Java heap space
     * 当配置为:[-Xms20m -Xmx20m]时,执行信息:Heap dump file created [27931458 bytes in 0.604 secs],内存中存放了:810325个对象
     * 当配置为:[-Xms10m -Xmx10m]时,执行信息:Heap dump file created [13075518 bytes in 0.261 secs],内存中存放了:360145个对象
     * 分析.hprof工具为:MemoryAnalyzer
     * 结论:
     * <li>
     * 通过配置-Xms参数设置堆最小值,通过-Xmx设置堆最大值,当堆使用大于最大值得时候就会抛出OutOfMemoryError。
     * </li>
     * <li>
     * 堆是用于存储内存对象的,当我们写代码的时候,生成的每一个对象都会放在堆中。
     * </li>
     * @author FDD
     *
     */
    public class HeapOOM {
        static class HeapObject implements Serializable{
            
        }
        
        public static void main(String[] args) throws Exception {
            List<HeapObject> list = new ArrayList<HeapObject>();
            try {
                //计算一个对象大小。按照序列化方式计算
    //            ByteArrayOutputStream bos = new ByteArrayOutputStream();
    //            ObjectOutputStream oos = new ObjectOutputStream(bos);
    //            oos.writeObject(new HeapObject());
    //            System.out.println(bos.toByteArray().length);
    //            oos.close();
    //            bos.close();
                while(true){
                    list.add(new HeapObject());
                }
            } catch (Throwable e) {
                System.out.println("List size is :" + list.size());
                e.printStackTrace();
            }
        }
    }

    执行结果:

    java.lang.OutOfMemoryError: Java heap space
    Dumping heap to java_pid44464.hprof ...
    Heap dump file created [27864823 bytes in 0.523 secs]
    List size is :810325
    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 com.wlzjdm.jvm.learning.HeapOOM.main(HeapOOM.java:42)

    方法栈溢出

    package com.wlzjdm.jvm.learning;
    
    /**
     * VM Args: -Xss128K
     * 模拟测试本地方法栈溢出情况,抛出:java.lang.StackOverflowError<br>
     * <ul>
     * 配置为:-Xss1K时,stack大小为19015
     * </ul>
     * <ul>
     * 配置为:-Xss2K时,stack大小为18434
     * </ul>
     * <ul>
     * 配置为:-Xss128K时,stack大小为987
     * </ul>
     * <ul>
     * 配置为:-Xss512K时,stack大小为8486
     * </ul>
     * <ul>
     * 配置为:-Xss1M时,      stack大小为19264
     * </ul>
     * <ul>
     * 配置为:-Xss20M时,   stack大小为1270590
     * </ul>
     * <b>结论:</b>
     * <li>
     * -Xss用于配置本地方法栈大小
     * </li>
     * <li>
     * 栈是方法区使用的,很容易理解,一般一个方法都是后进先出的。
     * </li>
     * @author FDD
     *
     */
    public class JavaVMStackESOF {
        private int stackLength = 1;
        
        public void stackLeak(){
            stackLength ++;
            stackLeak();
        }
        
        public static void main(String[] args) throws Throwable {
            JavaVMStackESOF javaVMStackESOF = new JavaVMStackESOF();
            try {
                javaVMStackESOF.stackLeak();
            } catch (Throwable e) {
                System.out.println("Stack Length is :" + javaVMStackESOF.stackLength);
                throw e;
            }
        }
    }

    执行结果:

    Stack Length is :11429
    Exception in thread "main" java.lang.StackOverflowError
        at com.wlzjdm.jvm.learning.JavaVMStackESOF.stackLeak(JavaVMStackESOF.java:38)
        at com.wlzjdm.jvm.learning.JavaVMStackESOF.stackLeak(JavaVMStackESOF.java:39)
        at com.wlzjdm.jvm.learning.JavaVMStackESOF.stackLeak(JavaVMStackESOF.java:39)
        at com.wlzjdm.jvm.learning.JavaVMStackESOF.stackLeak(JavaVMStackESOF.java:39)
        at com.wlzjdm.jvm.learning.JavaVMStackESOF.stackLeak(JavaVMStackESOF.java:39)
    ………………

    永久代溢出

    package com.wlzjdm.jvm.learning;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * VM Args: -XX:PermSize=1M -XX:MaxPermSize=1M <br>
     * 说明:此项是配置永久代大小,在JDK1.6及之前,常量是存放于永久代中,所以限制永久代大小就简介限制常量池大小<br>
     * <code>
     *  <b>JDK1.6 Run Result :</b>
     *  <li> 
     *      Configuration -XX:PermSize=10M -XX:MaxPermSize=10M : 116804 Exception in thread "main" java.lang.OutOfMemoryError: PermGen space <br>
     *  </li>
     *  <li> 
     *      Configuration -XX:PermSize=20M -XX:MaxPermSize=20M : 280572 Exception in thread "main" java.lang.OutOfMemoryError: PermGen space <br>
     *  </li>
     *  <b>JDK > 1.7 Run Result :</b>
     *  <li> 
     *      可以一直运行下去
     *  </li>
     *  <b>结论:-XX:PermSize和-XX:MaxPermSzie是配置永久代的大小,在JDK1.6及之前,常量是存放于永久代中,所以可以通过配置永久代大小配置敞亮池,而在JDK1.6之后,逐步剔除永久代的事情。</b>
     * </code>
     * @author FDD
     *
     */
    public class RuntimeConstantPoolOOM {
        public static void main(String[] args) throws Throwable {
            List<String> list = new ArrayList<String>();
            
            int i = 0;
            
            try {
                while(true){
                    list.add(String.valueOf(i++).intern());
                }
            } catch (Throwable e) {
                System.out.println(list.size());
                throw e;
            }
        }
    }

    运行结果:

    280572
    Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
        at java.lang.String.intern(Native Method)
        at com.wlzjdm.jvm.learning.RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java:34)

    方法区溢出

    package com.wlzjdm.jvm.learning;
    
    import java.lang.reflect.Method;
    
    import net.sf.cglib.proxy.Callback;
    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 <br>
     * 此例用来模拟方法区异常,
     * 结果:OutOfMemoryError: PermGen space
     * @author FDD
     *
     */
    public class JavaMethodAreaOOM {
        public static void main(String[] args) throws Exception {
            while (true) {
                Enhancer enhancer = new Enhancer();
                enhancer.setSuperclass(OOMObject.class);
                enhancer.setUseCache(false);
                enhancer.setCallback(new MethodInterceptor() {
    
                    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                        return proxy.invoke(obj, args);
                    }
                });
                enhancer.create();
            }
        }
    
        static class OOMObject {
            public OOMObject() {
            }
        }
    }

    运行结果:

    Exception in thread "main" net.sf.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null
        at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:256)
        at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:378)
        at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:286)
        at com.wlzjdm.jvm.learning.JavaMethodAreaOOM.main(JavaMethodAreaOOM.java:29)
    Caused by: java.lang.reflect.InvocationTargetException
        at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:395)
        at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:237)
        ... 3 more
    Caused by: java.lang.OutOfMemoryError: PermGen space
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
        ... 8 more

    本机内存溢出

    package com.wlzjdm.jvm.learning;
    
    import java.lang.reflect.Field;
    
    import sun.misc.Unsafe;
    
    /**
     * VM Args: -Xmx20M -XX:MaxDirectMemorySize=10M</br>
     * JVM可以通过指定-XX:MaxDirectMemorySize指定,如果不指定,那么默认与JAVA堆最大值-Xmx一样。<br>
     * 执行结果:Exception in thread "main" java.lang.OutOfMemoryError
     * @author FDD
     *
     */
    public class DirectMemoryOOM {
        private static final int _1MB = 1024 * 1024;
        
        public static void main(String[] args) throws Exception {
            Field unsaveField = Unsafe.class.getDeclaredFields()[0];
            unsaveField.setAccessible(true);
            Unsafe unsafe = (Unsafe) unsaveField.get(null);
            while(true){
                unsafe.allocateMemory(_1MB);
            }
        }
    
    }

    运行结果

    Exception in thread "main" java.lang.OutOfMemoryError
        at sun.misc.Unsafe.allocateMemory(Native Method)
        at com.wlzjdm.jvm.learning.DirectMemoryOOM.main(DirectMemoryOOM.java:22)

    总结

    对于一个内存溢出的问题,我们一定要搞清楚是那块儿内存溢出,只有明白那块内存溢出,才知道系统的数据存储分配,然后通过分析Heap Dump文件得出具体的类,根据错误类型定位到具体的问题点(注意:DirectMemory导致的内存溢出,一个明显的特征是在Heap Dump文件中不会看见明显的异常)。

  • 相关阅读:
    js命名空间笔记
    css3兼容性问题归纳
    flexbox-CSS3弹性盒模型flexbox完整版教程
    JavaScript 预解析
    消除页面上的链接虚线框
    图片压缩之 PNG
    常见的前端优化技巧有哪些
    for-of循环和for-in循环的区别
    函数式编程初探
    js中同步与异步处理方法
  • 原文地址:https://www.cnblogs.com/wlzjdm/p/6876974.html
Copyright © 2020-2023  润新知