• 工作中最常见的6种OOM问题


    前言

    最近我写的几篇线上问题相关的文章:《糟糕,CPU100%了》《如何防止被恶意刷接口》《我调用第三方接口遇到的13大坑》发表之后,在全网广受好评。

    今天接着线上问题这个话题,跟大家一起聊聊线上服务出现OOM问题的6种场景,希望对你会有所帮助。

    1 堆内存OOM

    堆内存OOM是最常见的OOM了。

    出现堆内存OOM问题的异常信息如下:

    java.lang.OutOfMemoryError: Java heap space
    

    此OOM是由于JVM中heap的最大值,已经不能满足需求了。

    举个例子:

    public class HeapOOMTest {
    
        public static void main(String[] args) {
            List list = Lists.newArrayList();
            while (true) {
                list.add(new HeapOOMTest());
            }
        }
    }
    

    这里创建了一个list集合,在一个死循环中不停往里面添加对象。

    执行结果:

    出现了java.lang.OutOfMemoryError: Java heap space的堆内存溢出。

    很多时候,excel一次导出大量的数据,获取在程序中一次性查询的数据太多,都可能会出现这种OOM问题。

    我们在日常工作中一定要避免这种情况。

    2 栈内存OOM

    有时候,我们的业务系统创建了太多的线程,可能会导致栈内存OOM。

    出现堆内存OOM问题的异常信息如下:

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

    给大家举个例子:

    public class StackOOMTest {
        public static void main(String[] args) {
            while (true) {
                new Thread().start();
            }
        }
    }
    

    使用一个死循环不停创建线程,导致系统产生了大量的线程。

    执行结果:

    如果实际工作中,出现这个问题,一般是由于创建的线程太多,或者设置的单个线程占用内存空间太大导致的。

    建议在日常工作中,多用线程池,少自己创建线程,防止出现这个OOM。

    3 栈内存溢出

    我们在业务代码中可能会经常写一些递归调用,如果递归的深度超过了JVM允许的最大深度,可能会出现栈内存溢出问题。

    出现栈内存溢出问题的异常信息如下:

    java.lang.StackOverflowError
    

    例如:

    public class StackFlowTest {
        public static void main(String[] args) {
            doSamething();
        }
    
        private static void doSamething() {
            doSamething();
        }
    }
    

    执行结果:

    出现了java.lang.StackOverflowError栈溢出的错误。

    我们在写递归代码时,一定要考虑递归深度。即使是使用parentId一层层往上找的逻辑,也最好加一个参数控制递归深度。防止因为数据问题导致无限递归的情况,比如:id和parentId的值相等。

    4 直接内存OOM

    直接内存不是虚拟机运行时数据区的一部分,也不是《Java虚拟机规范》中定义的内存区域。

    它来源于NIO,通过存在堆中的DirectByteBuffer操作Native内存,是属于堆外内存,可以直接向系统申请的内存空间。

    出现直接内存OOM问题时异常信息如下:

    java.lang.OutOfMemoryError: Direct buffer memory
    

    例如下面这样的:

    public class DirectOOMTest {
    
        private static final int BUFFER = 1024 * 1024 * 20;
    
        public static void main(String[] args) {
            ArrayList list = new ArrayList<>();
            int count = 0;
            try {
                while (true) {
                    // 使用直接内存
                    ByteBuffer byteBuffer = ByteBuffer.allocateDirect(BUFFER);
                    list.add(byteBuffer);
                    count++;
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            } finally {
                System.out.println(count);
            }
        }
    }
    

    执行结果:

    会看到报出来java.lang.OutOfMemoryError: Direct buffer memory直接内存空间不足的异常。

    5 GC OOM

    GC OOM是由于JVM在GC时,对象过多,导致内存溢出,建议调整GC的策略。

    出现GC OOM问题时异常信息如下:

    java.lang.OutOfMemoryError: GC overhead limit exceeded
    

    为了方便测试,我先将idea中的最大和最小堆大小都设置成10M:

    -Xmx10m -Xms10m
    

    例如下面这个例子:

    public class GCOverheadOOM {
        public static void main(String[] args) {
            ExecutorService executor = Executors.newFixedThreadPool(5);
            for (int i = 0; i < Integer.MAX_VALUE; i++) {
                executor.execute(() -> {
                    try {
                        Thread.sleep(10000);
                    } catch (InterruptedException e) {
                    }
                });
            }
        }
    }
    

    执行结果:

    出现这个问题是由于JVM在GC的时候,对象太多,就会报这个错误。

    我们需要改变GC的策略。

    在老代80%时就是开始GC,并且将-XX:SurvivorRatio(-XX:SurvivorRatio=8)和-XX:NewRatio(-XX:NewRatio=4)设置的更合理。

    最近就业形式比较困难,为了感谢各位小伙伴对苏三一直以来的支持,我特地创建了一些工作内推群, 看看能不能帮助到大家。

    你可以在群里发布招聘信息,也可以内推工作,也可以在群里投递简历找工作,也可以在群里交流面试或者工作的话题。

    进群方式

    添加苏三的私人微信:su_san_java,备注:博客园+所在城市,即可加入。

    6 元空间OOM

    JDK8之后使用Metaspace来代替永久代,Metaspace是方法区在HotSpot中的实现。

    Metaspace不在虚拟机内存中,而是使用本地内存也就是在JDK8中的ClassMetadata,被存储在叫做Metaspace的native memory。

    出现元空间OOM问题时异常信息如下:

    java.lang.OutOfMemoryError: Metaspace
    

    为了方便测试,我修改一下idea中的JVM参数,增加下面的配置:

    -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m
    

    指定了元空间和最大元空间都是10M。

    接下来,看看下面这个例子:

    public class MetaspaceOOMTest {
        static class OOM {
        }
    
        public static void main(String[] args) {
            int i = 0;
            try {
                while (true) {
                    i++;
                    Enhancer enhancer = new Enhancer();
                    enhancer.setSuperclass(OOM.class);
                    enhancer.setUseCache(false);
                    enhancer.setCallback(new MethodInterceptor() {
                        @Override
                        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                            return methodProxy.invokeSuper(o, args);
                        }
                    });
                    enhancer.create();
                }
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
    }
    

    执行结果:

    程序最后会报java.lang.OutOfMemoryError: Metaspace的元空间OOM。

    这个问题一般是由于加载到内存中的类太多,或者类的体积太大导致的。

    好了,今天的内容先分享到这里,下一篇文章重点给大家讲讲,如何用工具定位OOM问题,敬请期待。

    最后说一句(求关注,别白嫖我)

    如果这篇文章对您有所帮助,或者有所启发的话,帮忙扫描下发二维码关注一下,您的支持是我坚持写作最大的动力。
    求一键三连:点赞、转发、在看。
    关注公众号:【苏三说技术】,在公众号中回复:面试、代码神器、开发手册、时间管理有超赞的粉丝福利,另外回复:加群,可以跟很多BAT大厂的前辈交流和学习。

  • 相关阅读:
    sylixos 5.0.4 ecs
    数字展厅是什么,数字展厅有哪些优势?
    node进程管理工具 pm2 常用操作命令
    计算机专硕变简单,只考一门数据结构!陕西理工大学计算机考研改考
    烽火推系统源码,抖音矩阵系统源码独立部署 TELL
    美林投资时钟策略如何运用?
    Ladder Side-Tuning:预训练模型的“过墙梯”
    使用vTESTstudio将CANoe项目导入vTESTstudio_02进行编程
    c++中的deque容器和queue容器
    Unity 之 定时调用函数的方法
  • 原文地址:https://www.cnblogs.com/12lisu/p/18079649