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)
  • 相关阅读:
    在线JS代码调试网址
    NVM在windows系统下载及安装
    浏览器【插件】【扩展】下载安装
    JavaScript 秘密花园
    HTTP 协议
    移动端:zepto框架
    移动端:移动端事件
    移动端:移动端页面布局
    移动端:Flex弹性盒布局
    移动端:自适应和响应式布局
  • 原文地址:https://www.cnblogs.com/mengY/p/12249295.html
Copyright © 2011-2022 走看看