zoukankan      html  css  js  c++  java
  • Java-JVM OutOfMemory 情况(JDK8)

    JVM 运行时内存结构(Run-Time Data Areas)

    内存溢出分为两大类:OutOfMemoryError 和 StackOverflowError。

    一、HeapOomError (JVM 堆内存溢出)

    -Xms:初始值
    -Xmx:最大值
    -Xmn:最小值

    public static void main(String[] args) {
        List<byte[]> list = new ArrayList<>();
        int i = 0;
        while (true) {
            list.add(new byte[8 * 1024 * 1024]);
            System.out.println(++i);
        }
    }

    二、MemoryLeakOomError(JVM 堆内存泄漏)

    Java 语言中是指,未使用的对象仍然在 JVM 堆空间中存在,任保留着引用,无法被 GC。不停的堆积最终触发堆内存溢出。

    三、OverheadLimitOomError(垃圾回收超时内存溢出)

    # JDK6 新增错误类型。当 GC 为释放很小空间占用大量时间时抛出。
    # (超过 98% 以上的时间去释放小于 2% 的堆空间)
    java.lang.OutOfMemoryError: GC overhead limit exceeded
    
    # 关闭上述检查功能,通常不治本,最终会 Java heap space。应查看系统是否有使用大内存的代码或死循环
    -XX:-UseGCOverheadLimit

    static class Key {
        Integer id;
        Key(Integer id) {
            this.id = id;
        }
        // @Override
        // public int hashCode() {
        //     return id.hashCode();
        // }
        //
        // @Override
        // public boolean equals(Object o) {
        //     boolean response = false;
        //     if (o instanceof Key) {
        //         response = (((Key) o).id).equals(this.id);
        //     }
        //     return response;
        // }
    }
    
    public static void main(String[] args) {
        Map<Key, String> m = new HashMap<>();
        int x = 0;
        while (true) {
            for (int i = 0; i < 500; i++) {
                if (!m.containsKey(new Key(i))) {
                    m.put(new Key(i), "Number:" + i);
                }
            }
            System.out.println(x++);
        }
    }

    public static void main(String[] args) {
        Map map = System.getProperties();
        Random r = new Random();
        while (true) {
            map.put(r.nextInt(), "Oracle Java");
        }
    }

    四、MetaSpaceOomError(Metaspace内存溢出)

    元空间的溢出,系统会抛出 java.lang.OutOfMemoryError: Metaspace。出现该错误的原因是类非常多或引用的 jar 包非常多或者通过动态代码生成类加载等方法,导致元空间的内存占用很大。

    JDK8 开始,方法区的实现由永久代(PermGen)变成了元空间(Metaspace)。Metaspace 使用的是本地内存,而不是堆内存,也就是说在默认情况下 Metaspace 的大小只与本地内存大小有关。

    # 初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整
    # 如果释放了大量的空间,就适当降低该值。
    # 如果释放了很少的空间,那么在不超过 MaxMetaspaceSize 下适当提高该值。
    -XX:MetaspaceSize
    
    # 最大空间,默认没有限制。
    # 防止因为某些情况导致 Metaspace 无限的使用本地内存,影响到其他程序。应设置大小。
    -XX:MaxMetaspaceSize
    
    
    # Metaspace 增长时的最大幅度。
    -XX:MaxMetaspaceExpansion
    
    # Metaspace 增长时的最小幅度。
    -XX:MinMetaspaceExpansion
    
    
    # 当进行过Metaspace GC之后, 会计算当前Metaspace的空闲空间比,如果空闲比大于这个参数,那么虚拟机会释放 Metaspace 的部分空间。在本机该参数的默认值为70,也就是70%。
    -XX:MaxMetaspaceFreeRatio
    
    # 当进行过Metaspace GC之后,会计算当前 Metaspace 的空闲空间比,如果空闲比小于这个参数,那么虚拟机将增长 Metaspace 的大小。在本机该参数的默认值为40,也就是40%。
    # 设置该参数可以控制 Metaspace 的增长的速度,太小的值会导致 Metaspace 增长的缓慢,Metaspace 的使用逐渐趋于饱和,可能会影响之后类的加载。而太大的值会导致 Metaspace 增长的过快,浪费内存。
    -XX:MinMetaspaceFreeRatio

    public static void main(String[] args) {
        ClassLoadingMXBean loadingBean = ManagementFactory.getClassLoadingMXBean();
        // 借助 CGlib 来动态地生成大量的 Class
        while (true) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(MetaSpaceOomError.class);
            enhancer.setCallbackTypes(new Class[]{Dispatcher.class, MethodInterceptor.class});
            enhancer.setCallbackFilter(new CallbackFilter() {
                @Override
                public int accept(Method method) {
                    return 1;
                }
    
                @Override
                public boolean equals(Object obj) {
                    return super.equals(obj);
                }
            });
            Class clazz = enhancer.createClass();
    
            System.out.println(clazz.getName() + "===================================");
            // 显示数量信息(共加载过的类型数目,当前还有效的类型数目,已经被卸载的类型数目)
            System.out.println("total:" + loadingBean.getTotalLoadedClassCount());
            System.out.println("active:" + loadingBean.getLoadedClassCount());
            System.out.println("unloaded:" + loadingBean.getUnloadedClassCount());
        }
    }

    五、DirectoryMemoryOomError(直接内存内存溢出)

    在使用 ByteBuffer 中的 allocateDirect() 的时候会出现,很多 Java NIO(如 netty)的框架中被封装为其他的方法,出现该问题时会抛出 java.lang.OutOfMemoryError: Direct buffer memory。

    如果你在直接或间接使用了 ByteBuffer 中的 allocateDirect 方法,而不做 clear 就会出现类似的问题。

    # 堆外内存最大值
    -XX:MaxDirectMemorySize

    private static int ONE_MB = 1024 * 1024;
    private static int index = 0;
    
    public static void main(String[] args) {
        try {
            System.out.println(VM.maxDirectMemory() / ONE_MB);
            ByteBuffer.allocateDirect(ONE_MB * 100);
    
            // 直接内存操作
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            Unsafe unsafe = (Unsafe) field.get(null);
            while (true) {
                index++;
                // 分配内存
                long l = unsafe.allocateMemory(ONE_MB);
                // 释放内存
                unsafe.freeMemory(l);
            }
        } catch (Exception | Error e) {
            System.out.println("index:" + index);
            e.printStackTrace();
        }
    }

    六、StackOomError(栈内存溢出)

    当一个线程执行一个Java方法时,JVM将创建一个新的栈帧并且把它push到栈顶。

    当一个方法递归调用自己时,每层调用都需要创建一个新的栈帧。当栈中越来越多的内存将随着递归调用而被消耗,最终造成 java.lang.StackOverflowError。

    # 每个线程的堆栈大小
    -Xss

    private static int num = 1;
    
    public void testStack() throws StackOverflowError {
        num++;
        this.testStack();
    }
    
    public static void main(String[] agrs) {
        try {
            StackOomError t = new StackOomError();
            t.testStack();
        } catch (StackOverflowError stackOverflowError) {
            System.out.println(num);
            stackOverflowError.printStackTrace();
        }
    }

    七、ArrayLimitOomError(数组超限内存溢出)

    JVM 对应用程序所能分配数组最大大小是有限制的,Java 数组的索引是 int 类型,不同的平台限制有所不同。

    在为数组分配内存之前,会执行特定平台的检查:分配的数据结构是否在此平台是可寻址的。

    若数组长度超出系统上限就会造成 java.lang.OutOfMemoryError: Requested array size exceeds VM limit。

    public static void main(String[] args) {
        try {
            int[] arr = new int[Integer.MAX_VALUE];
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }


    https://segmentfault.com/a/1190000017226359

    https://blog.csdn.net/bolg_hero/article/details/78189621

  • 相关阅读:
    深入理解计算机系统 第六章 存储器层次结构 第二遍
    深入理解计算机系统 第六章 存储器层次结构
    深入理解计算机系统 第八章 异常控制流 Part2 第二遍
    深入理解计算机系统 第八章 异常控制流 part2
    深入理解计算机系统 第八章 异常控制流 Part1 第二遍
    深入理解计算机系统 第八章 异常控制流 part1
    深入理解计算机系统 第三章 程序的机器级表示 Part2 第二遍
    深入理解计算机系统 第三章 程序的机器级表示 part2
    深入理解计算机系统 第三章 程序的机器级表示 Part1 第二遍
    深入理解计算机系统 第三章 程序的机器级表示 part1
  • 原文地址:https://www.cnblogs.com/jhxxb/p/11100480.html
Copyright © 2011-2022 走看看