zoukankan      html  css  js  c++  java
  • 常见的六种OOM异常和错误

    1、java.lang.StackOverflowError

    报这个错误一般是由于方法深层次的调用,默认的线程栈空间大小一般与具体的硬件平台有关。栈内存为线程私有的空间,每个线程都会创建私有的栈内存。栈空间内存设置过大,创建线程数量较多时会出现栈内存溢出StackOverflowError。同时,栈内存也决定方法调用的深度,栈内存过小则会导致方法调用的深度较小,如递归调用的次数较少。

    Demo:

    public class StackOverFlowErrorDemo {
    
       static int i = 0;
    
        public static void main(String[] args) {
            stackOverflowErrorTest();
        }
    
        private static void stackOverflowErrorTest() {
    
            i++;
            System.out.println("这是第 "+i+" 次调用");
            stackOverflowErrorTest();
    
        }
    }
    //运行结果:
    。。。。
    这是第 6726 次调用
    这是第 6727 次调用
    Exception in thread "main" java.lang.StackOverflowError
    。。。。
    

    注意:这是一个Error!!!!

    2、java.lang.OutOfMemoryError: Java heap space

    Heap size 设置 JVM堆的设置是指:java程序执行过程中JVM能够调配使用的内存空间的设置。JVM在启动的时候会自己主动设置Heap size的值,其初始空间(即-Xms)是物理内存的1/64最大空间(-Xmx)是物理内存的1/4。能够利用JVM提供的-Xmn -Xms -Xmx等选项可进行设置。Heap size 的大小是Young Generation 和Tenured Generaion 之和。

    Demo:

    public class OOMHeapSpaceDemo {
        public static void main(String[] args) {
            byte[] bytes = new byte[30*1024*1024];
        }
    }
    

    然后修改堆内存的初始容量和最大容量为5MB

    运行程序,查看结果:

    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    	at jvm.OOMHeapSpaceDemo.main(OOMHeapSpaceDemo.java:7)
    

    注意:这是一个Error!!!!

    3、java.lang.OutOfMemoryError:GC overhead limit exceeded

    GC回收时间过长时会抛出的OutOfMemory。过长是指,超过98%的时间都在用来做GC并且回收了不到2%的堆内存。连续多次的GC,都回收了不到2%的极端情况下才会抛出。假如不抛出GC overhead limit 错误会发生什么事情呢?那就是GC清理出来的一点内存很快又会被再次填满,强迫GC再次执行,这样造成恶性循环,CPU的使用率一直很高,但是GC没有任何的进展。

    Demo:

    /**
     * 调整虚拟机的参数:
     * -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m
     */
    public class GCOverHeadDemo {
        public static void main(String[] args) {
            int i= 0;
            List<String> list = new ArrayList<>();
            while (true){
                list.add(String.valueOf(++i).intern());
                System.out.println(i);
    
            }
        }
    }
    
    
    //运行结果:
    [Full GC (Ergonomics) [PSYoungGen: 1024K->1024K(2048K)] [ParOldGen: 7101K->7099K(7168K)] 8125K->8123K(9216K), [Metaspace: 3264K->3264K(1056768K)], 0.0296282 secs] [Times: user=0.08 sys=0.00, real=0.03 secs] 
    
    Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
    

    可以看到GC前后内存占用时一样的,也就是说虽然在频繁的GC,但是并没有起到什么作用,并没有回收回来多少的内存。

    4、java.lang.OutOfMemoryError:Direct buffer memory

    写NIO程序经常使用到ByteBuffer来读取或者写入数据,这是一种基于通道与缓冲区的I/O方式。它可以使用Native函数库直接分配堆外内存,然后通过一个存储在java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中提高性能,因为避免了java堆和Native堆中来回复制数据。

    • ByteBuffer.allocate(capability) :这种方式是分配JVM堆内存,属于GC管辖范围之内。由于需要拷贝,所以速度相对较慢;
    • ByteBuffer.allocateDirect(capability):这种方式是直接分配OS本地内存,不属于GC管辖范围之内,由于不需要内存拷贝所以速度相对较快。

    但是如果不断分配本地内存,堆内存很少使用,那么JVM就不需要执行GC,DirectByteBuffer对象就不会被回收。这时候堆内存充足,但是本地内存已经用光了,再次尝试分配的时候就会出现OutOfMemoryError,那么程序就直接崩溃了。

    Demo:

    /**
     * JVM配置参数:
     * -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m
     */
    public class DirectBufferMemoryDemo {
        public static void main(String[] args) {
            System.out.println("配置的MaxDirectMemorySize"+sun.misc.VM.maxDirectMemory()/(double)1024/1024+" MB");
            ByteBuffer byteBuffer = ByteBuffer.allocateDirect(6*1024*1024);
        }
    }
    
    
    //运行结果:
    配置的MaxDirectMemorySize5.0 MB
    [GC (System.gc()) [PSYoungGen: 1785K->488K(2560K)] 1785K->728K(9728K), 0.0019042 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
    [Full GC (System.gc()) [PSYoungGen: 488K->0K(2560K)] [ParOldGen: 240K->640K(7168K)] 728K->640K(9728K), [Metaspace: 3230K->3230K(1056768K)], 0.0077924 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
    
    Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
    

    5、java.lang.OutOfMemoryError:unable to create new native thread

    准确的说,这一个异常是和程序运行的平台相关的。导致的原因:

    • 创建了太多的线程,一个应用创建多个线程,超过系统承载极限;
    • 服务器不允许应用程序创建这么多的线程,Linux系统默认的允许单个进程可以创建的线程数量是1024个,当创建多 线程数量多于这个数字的时候就会抛出此异常

    如何解决呢?

    • 想办法减少应用程序创建的线程的数量,分析应用是否真的需要创建这么多的线程。如果不是,改变代码将线程数量降到最低;
    • 对于有的应用,确实需要创建很多的线程,远超过Linux限制的1024个 限制,那么可以通过修改Linux服务器的配置,扩大Linux的默认限制。

    Demo:

    public class UnableCreateNewThreadDemo {
        public static void main(String[] args) {
            for (int i = 1; ;i++){
                System.out.println("i = " +i);
                new Thread(()->{
                    try {
                        Thread.sleep(Integer.MAX_VALUE);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                },i+"").start();
            }
        }
    }
    
    //运行结果:
    。。。。
    i = 92916
    i = 92917
    Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
    。。。。
    

    6、java.lang.OutOfMemoryError:MetaSpace

    元空间的本质和永久代类似,都是对JVM规范中的方法区的实现。不过元空间与永久代之间最大的区别在于:元空间不在虚拟机中,而是使用的本地内存。因此,默认情况下,元空间的大小仅仅受到本地内存的限制 。

    元空间存放了以下的内容:

    • 虚拟机加载的类信息;
    • 常量池;
    • 静态变量;
    • 即时编译后的代码

    模拟MetaSpace空间溢出,我们不断生成类往元空间里灌,类占据的空间总是会超过MetaSpace指定的空间大小的

    查看元空间的大小:java -XX:+PrintFlagsInitial

    Demo:

    /**
     * JVM参数配置:
     * -XX:MetaSapceSize=5m
     */
    public class MetaSpaceDemo {
        public static void main(String[] args) {
            for (int i = 0; i < 100_000_000; i++) {
                generate("eu.plumbr.demo.Generated" + i);
            }
        }
        public static Class generate(String name) throws Exception {
            ClassPool pool = ClassPool.getDefault();
            return pool.makeClass(name).toClass();
        }
    }
    


  • 相关阅读:
    POJ1486 Sorting Slides 二分图or贪心
    POJ2060 Taxi Cab Scheme 最小路径覆盖
    POJ3083 Children of the Candy Corn 解题报告
    以前的文章
    POJ2449 Remmarguts' Date K短路经典题
    这一年的acm路
    POJ3014 Asteroids 最小点覆盖
    POJ2594 Treasure Exploration 最小路径覆盖
    POJ3009 Curling 2.0 解题报告
    POJ2226 Muddy Fields 最小点集覆盖
  • 原文地址:https://www.cnblogs.com/simon-1024/p/12221917.html
Copyright © 2011-2022 走看看