JAVA OOM问题

什么是OOM

官方说明: Thrown when the Java Virtual Machine cannot allocate an object because it is out of memory, and no more memory could be made available by the garbage collector. 即: 当 Java 虚拟机由于内存不足而无法分配对象,并且垃圾收集器无法提供更多内存时抛出。
注意:java.lang.OutOfMemoryError 并非一个异常,而是error。

内存溢出和内存泄露

内存溢出(Memory Overflow):指程序在运行时需要的内存空间超过了系统所能提供的内存空间。这通常发生在程序请求分配大量内存或者存在无限循环等情况下。

内存泄露(Memory Leak):指程序在运行过程中没有正确释放不再使用的内存空间。

常见的OOM类型

  1. java.lang.OutOfMemoryError: Java heap space(堆内存溢出)
    • 问题
      Java堆空间存储了对象实例,当Java应用程序中创建的对象数量超过了堆空间的限制时,就会发生Java Heap Space内存不足的OOM错误。这通常是最常见的OOM类型。
    • 解决
      增加Java堆空间大小:通过调整JVM参数 -Xmx 和 -Xms 来分别增加Java堆的最大和初始大小;
      优化内存使用:检查应用程序中是否存在内存泄漏或者内存使用不当的情况,并进行优化。
    • 举例
      设置-Xmx 和 -Xms 参数
      在这里插入图片描述
      代码:
public static void main(String[] args) {

        List<String> arr = new ArrayList<>();

        while (true) {
            arr.add(new String());
        }
    }

运行结果:
在这里插入图片描述
可使用Jprofile工具。

  1. java.lang.OutOfMemoryError: PermGen space(永久代溢出)

    • 问题
      在Java 7及更早的版本中,永久代(Permanent Generation)用于存储类信息、方法信息等。当应用程序动态地加载大量Class或创建大量动态代理时,可能会导致PermGen Space内存不足的OOM错误。在Java 8及之后的版本中,永久代已被元数据区(Metaspace)取代。
    • 解决
      可以通过 -XX:MaxPermSize 参数,尝试增加永久代的大小,比如:-XX:MaxPermSize=256m,但这在Java 8及之后的版本中不再适用。
  2. Java.lang.OutOfMemoryError:Metaspace(元空间溢出)
    注:方法区的实现,JDK1.7之前是永久代,JDK1.8之后是元空间。

    • 问题
      从 Java 8 开始,永久代被元空间取代。元空间溢出的原因可能有大量类或类元数据的加载、Metaspace 内存参数设置不当、动态生成的代理类过多。类元数据:指描述类本身信息的数据,比如类的结构、字段、方法、继承关系等。
    • 举例
      设置jvm参数,限制元空间大小为30M
-XX:MaxMetaspaceSize=30M

以下程序可能导致元空间内存溢出:

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class MetaspaceOOM {
    public static void main(String[] args) {
        while (true) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(OOMObject.class);
            enhancer.setUseCache(false);
            enhancer.setCallback(new MethodInterceptor() {
                public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                    return proxy.invokeSuper(obj, args);
                }
            });
            enhancer.create(); // 动态生成类并加载
        }
    }

    static class OOMObject {
        // 这里可以是一些业务方法
    }
}
  • 运行结果
    在这里插入图片描述
  • 解决
    增加元数据区的大小:通过调整 -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize 参数来增加Metaspace的初始和最大大小。
    优化类加载和动态代理使用:减少动态加载类和动态创建代理的频率,或者考虑缓存已加载的类。
  1. java.lang.StackOverflowError(栈溢出)
    • 问题
      深度递归或大量嵌套调用可能导致栈溢出。解决方法是检查代码中的递归或嵌套结构,优化算法或增加栈大小。
public class StackOverflowTest {
    public static void main(String[] args) {
        recursiveMethod(0);
    }

    public static void recursiveMethod(int i) {
        System.out.println("Recursive method call: " + i);
        recursiveMethod(i + 1); // 递归调用自身
    }
}

运行结果:
在这里插入图片描述

  • 解决
    减少递归或方法调用的深度:优化代码以减少方法调用层次或递归深度,或者转换为迭代方式实现。
    增加栈空间大小:通过调整JVM参数 -Xss 来增加线程栈的大小,但要注意栈空间大小设置过大可能导致线程数受限。

OOM分析

Heap Dump(堆转储文件)是一个 Java 进程在某个时间点上的内存快照。Heap Dump 是有着多种类型的。不过总体上 heap dump 在触发快照的时候都保存了 java 对象和类的信息。通常在写 heap dump 文件前会触发一次 FullGC, 所以 heap dump 文件中保存的是 FullGC 后留下的对象信息。通过设置如下的 JVM 参数, 可以在发生 OutOfMemoryError 后获取到一份 HPROF 二进制 Heap Dump 文件:
-XX:+HeapDumpOnOutOfMemoryError
dump文件分析工具:
Jprofile:由 ej-technologies 公司开发的一款性能瓶颈分析工具。它功能丰富,具备一些免费软件所不具备的功能。Jprofiler 提供的主要功能有内存视图、CPU 视图、线程视图、堆遍历器(Heap Walker)等。使用方法可参考:https://zhuanlan.zhihu.com/p/109870776?utm_id=0
Arthas:阿里开源的 Java 生成环境诊断工具。使用方法:https://arthas.aliyun.com/doc/install-detail.html

  • 41
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值