zoukankan      html  css  js  c++  java
  • Java 内存管理机制:02 OOM异常

    OOM 异常 (OutOfMemoryError)

    Java 堆溢出

    出现标志:java.lang.OutOfMemoryError: Java heap space

    解决方法:

    • 先通过内存对象分析工具分析Dump出来的堆转存储快照,确认内存中的对象是否是必要的,级分清楚是出现了内存泄漏还是内存溢出;
    • 如果是内存泄漏,通过攻击查看泄漏对象到GC Root的引用链,定位出泄漏的位置;
    • 如果不存在泄漏,检查虚拟机对参数(-Xmx和-Xms)是否可以调大,检查代码中是否有哪些对象的生命周期过长,尝试减少程序运行期的内存消耗。

     虚拟机参数:-XX:HeapDumpOnOutOfMemoryError:让虚拟机在出现内存泄漏异常时 Dump 出当前的内存堆转储快照用于事后分析。

    例如:

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

    package com.mall.jvm;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * -Xms20m -Xmx20m  -XX:+HeapDumpOnOutOfMemoryError
     */
    public class HeapOOM {
    
        static class OOMObject{
    
        }
        public static void main(String[] args) {
            List<OOMObject> list = new ArrayList<>();
            while (true){
                list.add(new OOMObject());
            }
        }
    }

      运行结果

    java.lang.OutOfMemoryError: Java heap space
    Dumping heap to java_pid18004.hprof ...
    Heap dump file created [28624826 bytes in 0.092 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.mall.jvm.HeapOOM.main(HeapOOM.java:18)

    Java 虚拟机栈和本地方法栈溢出

    • 单线程下,栈帧过大,虚拟机容量过小都不会导致OutOfMemoryError,只会导致StackOverflowError(栈会比内存先爆掉),一般多线程才会出现OutOfMemoryError,因为线程本身要占用内存
    • 如果是多线程导致的OutOfMemoryError,在不能减少线程数或更换64位虚拟机的情况,只能通过减少最大堆和减少栈容量来换取更多的线程;(这个调节思路和 Java 堆出现 OOM 正好相反,Java 堆出现 OOM 要调大堆内存的设置值,而栈出现 OOM 反而要调小)

    例如:栈(StackOverflowError)

    如果线程请求的栈深度大于虚拟机栈所允许的最大深度,将会抛出StackOverflowError异常

    package com.mall.jvm;
    
    /**
     * -Xss128k
     */
    public class JavaVMStackSOF {
    
        private int stackLength = 1;
    
        public void stackLeak(){
            stackLength++;
            stackLeak();
        }
    
        public static void main(String[] args) {
            JavaVMStackSOF javaVMStackSOF = new JavaVMStackSOF();
            try {
                javaVMStackSOF.stackLeak();
            }catch (Exception e){
                System.out.println("stack length :" + javaVMStackSOF.stackLength);
                throw e;
    
            }
        }
    }
    

      运行结果:

    Exception in thread "main" java.lang.StackOverflowError
    	at com.mall.jvm.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:11)
    

      栈(OutOfMemoryError)

    如果虚拟机在拓展栈时无法申请到足够的空间则抛出OutOfMemoryError异常

    package com.mall.jvm;
    
    /**
     * -Xss2M
     */
    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 javaVMStackOOM = new JavaVMStackOOM();
            javaVMStackOOM.stackLeakByThread();
        }
    }

     方法区和运行时常量池溢出

    • 测试思路:产生大量的类去填满方法区,直到溢出;
    • 在经常动态生成大量 Class 的应用中,如 Spring 框架(使用 CGLib 字节码技术),方法区溢出是一种常见的内存溢出,要特别注意类的回收状况。

    直接内存溢出

    • 出现特征:Heap Dump 文件中看不见明显异常,程序中直接或间接用了 NIO;
    • 虚拟机参数:-XX:MaxDirectMemorySize,如果不指定,则和 -Xmx 一样。
    package com.ecut.exception;
    
    import sun.misc.Unsafe;
    
    import java.lang.reflect.Field;
    
    /**
     *  -Xmx20M -XX:MaxDirectMemorySize = 10M
     */
    public class DirectMemoryOOM {
        private  static final int _1MB = 1024*1024;
    
        public static void main(String[] args) throws 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 com.ecut.exception.DirectMemoryOOM.main(DirectMemoryOOM.java:18)
  • 相关阅读:
    BZOJ 3132: 上帝造题的七分钟 树状数组+差分
    PAT Advanced 1006 Sign In and Sign Out (25 分)
    PAT Advanced 1011 World Cup Betting (20 分)
    PAT Basic 1032 挖掘机技术哪家强 (20 分)
    PAT Basic 1028 人口普查 (20 分)
    PAT Basic 1004 成绩排名 (20 分)
    大数据数据库HBase(二)——搭建与JavaAPI
    PAT Advanced 1009 Product of Polynomials (25 分)(vector删除元素用的是erase)
    PAT Advanced 1002 A+B for Polynomials (25 分)(隐藏条件,多项式的系数不能为0)
    PAT Basic 1041 考试座位号 (15 分)
  • 原文地址:https://www.cnblogs.com/mengY/p/12249295.html
Copyright © 2011-2022 走看看