首页>>后端>>java->Java OOM认知

Java OOM认知

时间:2023-12-02 本站 点击:0

一. StackOverflowError

1.1 bug

publicclassStackOverflowErrorDemo{publicstaticvoidmain(String[]args){javaKeeper();}privatestaticvoidjavaKeeper(){javaKeeper();}}

JVM 虚拟机栈是有深度的,在执行方法的时候会伴随着入栈和出栈,上边的方法可以看到,main 方法执行后不停的递归,迟早把栈撑爆了

Exceptioninthread"main"java.lang.StackOverflowErroratoom.StackOverflowErrorDemo.javaKeeper(StackOverflowErrorDemo.java:15)

1.2 原因分析

无限递归循环调用(最常见原因),要时刻注意代码中是否有了循环调用方法而无法退出的情况

执行了大量方法,导致线程栈空间耗尽

方法内声明了海量的局部变量

native 代码有栈上分配的逻辑,并且要求的内存还不小,比如 java.net.SocketInputStream.read0 会在栈上要求分配一个 64KB 的缓存(64位 Linux)

1.3 解决方案

修复引发无限递归调用的异常代码, 通过程序抛出的异常堆栈,找出不断重复的代码行,按图索骥,修复无限递归 Bug

排查是否存在类之间的循环依赖(当两个对象相互引用,在调用toString方法时也会产生这个异常)

通过 JVM 启动参数 -Xss 增加线程栈内存空间, 某些正常使用场景需要执行大量方法或包含大量局部变量,这时可以适当地提高线程栈空间限制

二. Java heap space

Java 堆用于存储对象实例,我们只要不断的创建对象,并且保证 GC Roots 到对象之间有可达路径来避免 GC 清除这些对象,那随着对象数量的增加,总容量触及堆的最大容量限制后就会产生内存溢出异常。

Java 堆内存的 OOM 异常是实际应用中最常见的内存溢出异常。

2.1 bug

/***JVM参数:-Xmx12m*/publicclassJavaHeapSpaceDemo{staticfinalintSIZE=2*1024*1024;publicstaticvoidmain(String[]a){int[]i=newint[SIZE];}}

代码试图分配容量为 2M 的 int 数组,如果指定启动参数 -Xmx12m,分配内存就不够用,就类似于将 XXXL 号的对象,往 S 号的 Java heap space 里面塞。

Exceptioninthread"main"java.lang.OutOfMemoryError:Javaheapspaceatoom.JavaHeapSpaceDemo.main(JavaHeapSpaceDemo.java:13)

2.2 原因分析

请求创建一个超大对象,通常是一个大数组

超出预期的访问量/数据量,通常是上游系统请求流量飙升,常见于各类促销/秒杀活动,可以结合业务流量指标排查是否有尖状峰值

过度使用终结器(Finalizer),该对象没有立即被 GC

内存泄漏(Memory Leak),大量对象引用没有释放,JVM 无法对其自动回收,常见于使用了 File 等资源没有回收

2.3 解决方案

针对大部分情况,通常只需要通过 -Xmx 参数调高 JVM 堆内存空间即可。如果仍然没有解决,可以参考以下情况做进一步处理:

如果是超大对象,可以检查其合理性,比如是否一次性查询了数据库全部结果,而没有做结果数限制

如果是业务峰值压力,可以考虑添加机器资源,或者做限流降级。

如果是内存泄漏,需要找到持有的对象,修改代码设计,比如关闭没有释放的连接

内存泄露和内存溢出

内存溢出(out of memory),是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个 Integer,但给它存了 Long 才能存下的数,那就是内存溢出。

内存泄露( memory leak),是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。

memory leak 最终会导致 out of memory!


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/java/10451.html