常见OOM异常及原因

本文探讨了Java中常见的内存溢出问题,包括堆内存溢出、栈溢出和元空间不足,提供了故障排查方法、代码示例和参数调整建议,同时介绍了如何避免GC效率低下和数组大小限制引发的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.堆内存溢出(Java heap space)

异常信息:java.lang.OutOfMemoryError: Java heap space
可能原因:死循环、递归层数太多、内存泄露。或者系统并发高请求量大内存太小。
解决方案:

  • 排查代码 jstack jmap jstat
  • 参数设置:-Xms1024m JVM启动内存初始大小 -Xmx3062m JVM启动内存的最大值

2.栈溢出(Thread Stack space)

异常信息:java.lang.StackOverflowError: Thread Stack space
可能原因:单线程调用方法次数太多,一般递归层数太多
解决方案:

  • 排查代码
  • 参数设置:-Xss512k 设置虚拟机栈大小

3.元空间不足(MetaSpace)

**异常信息:**java.lang.OutOfMemoryError: MetaSpace
**发生原因:**元空间不足
**解决方案:**增大元空间大小,配置参数-XX:MaxMetaspaceSize=xx

示例:
可在启动时配置的小一点,可模拟次异常, 比如设置 -XX:MaxMetaspaceSize=2m

注:

  • 元空间初始大小 默认21M
  • -XX:MaxMetaspaceSize=512m , 元空间最大可分配大小(一台机器多个服务可配置上限防止影响其他服务)
  • 也可能是代码问题,cglib动态代理生成的类过多塞满Metaspace导致溢出。

4.GC效率太低(GC overhead limit exceeded)

异常信息:java.lang.OutOfMemoryError: GC overhead limit exceeded
发生原因:

  • 垃圾收集器GC效率很低,jvm花费超过 98%的 CPU 时间来进行一次 GC,
  • 但是回收的内存却少于 2%的堆空间大小,并且GC连续超过5次都这样

示例代码

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

启动时加参数:-Xmx45m -XX:+UseParallelGC -XX:+PrintGCDetails运行一段时间,就会出现以下异常。
可通过增加参数-XX:-UseGCOverheadLimit可以避免这个异常,但其实是治标不治本,还需实际定位修改代码问题。

5.数组大小超过限制(VM limit)

**异常信息:**java.lang.OutOfMemoryError: Requested array size exceeds VM limit
发生原因:

  • 请求分配的数组太大,导致jvm空间不足
  • 请求的数组大于等于Integer.MAX_INT - 1

示例代码1 :直接抛出Requested array size exceeds VM limit

int[] arr = new int[Integer.MAX_VALUE - 1];

示例代码2 :先抛出 Java heap space后再抛出Requested array size exceeds VM limit

for (int i = 3; i >= 0; i--) {
    try {
        int[] arr = new int[Integer.MAX_VALUE-i];
        System.out.format("Successfully initialized an array with %,d elements.\n", Integer.MAX_VALUE-i);
    } catch (Throwable t) {
        t.printStackTrace();
    }
}

6.创建线程失败(Unable to create threads)

异常信息:java.lang.OutOfMemoryError: Unable to create native threads
可能原因:

  • 内存空间不够,jvm启动时参数-Xss指定每个线程占用的堆栈大小,如果内存不够,就会创建线程失败

  • 操作系统上ulimit中max user processes参数限制,这个参数指操作系统可以创建的全局线程数量

    • 可使用 ulimit -a | grep 'max user processes’命令可以查看,如下图
      在这里插入图片描述
  • 参数sys.kernel.threads-max限制,

    • 可通过命令cat /proc/sys/kernel/threads-max来查看,如下图
      在这里插入图片描述

    • 修改这个参数,需要在/etc/sysctl.conf文件,加入sys.kernel.threads-max = 10000

  • 参数sys.kernel.pid_max限制,这个参数只是每创建一个线程,都需要分配一个pid,当pid的值大于这个值时,就会创建失败。

    • 查看命令:cat /proc/sys/kernel/pid_max
      在这里插入图片描述

    • 修改这个参数,需要在/etc/sysctl.conf文件,加入sys.kernel.pid_max =10000

7.堆外内存溢出(Direct buffer memory)

异常信息:nio handle failed java.lang.OutOfMemoryError: Direct buffer memory
可能原因:堆外内存不足
解决方案:

  • JVM参数设置最大堆外内存:-XX:MaxDirectMemorySize
  • 申请堆外内存代码:
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);//申请堆外内存1k

1、如果堆外内存足够,就直接预留一部分内存
2、如果堆外内存不足,则将已经被 JVM 垃圾回收的 DirectBuffer 对象的堆外内存释放
3、如果进行一次堆外内存资源回收后,还不够进行本次堆外内存分配的话,则进行 System.gc()
4、如果 9 次尝试后依旧没有足够的可用堆外内存,则抛异常
原因可能是年轻代设置太小,并且关闭了系统gc调用(-XX:DisableExplicitGC)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员小强

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值