java处理oom_Java中常见的几个OOM异常的情况以及处理思路总结

除了PC(程序计数器)以外,Java虚拟机内存区域的都有可能发生OOM(OutOfMemoryError)。

Java堆溢出

Java堆是用于存储对象实例的,只要不断地创建对象,并且保证GC Roots到对象之间有可达路径避免垃圾回收机制被清楚,那么在对象数量到达最大堆的容量限制之后便会产生OOM异常。

解决思路

一般的手段是:先通过内存映像工具对Dump出来的堆转储快照进行分析,重点是确认内存中的对象是否是必要的,也就是要先分清楚到底是出现了内存泄漏还是内存溢出。

如果是内存泄漏,可进一步通过工具查看泄漏对象到GC Roots的引用链。这样就能够找到泄漏的对象是通过怎么样的路径与GC Roots相关联的导致垃圾回收机制无法将其回收。掌握了泄漏对象的类信息和GC Roots引用链的信息,就可以比较准确地定位泄漏代码的位置。

如果不存在泄漏,换句话说,就是内存中的对象确实必须存活着,那么此时就需要通过虚拟机的堆参数( -Xmx和-Xms)来适当调大参数;从代码上检查是否存在某些对象存活时间过长、持有时间过长的情况,尝试减少运行时内存的消耗。

-Xmx : 最大堆空间;

-Xms : 初始堆空间大小,如果初始堆空间耗尽,JVM会对堆空间扩容,其扩展上限为最大堆空间。通常-Xms与-Xmx设置为同样大小,避免扩容造成性能损耗。

虚拟机栈和本地方法栈溢出

由于在HotSpot虚拟机上并不区分虚拟机栈和本地方法栈,因此栈容量只能由-Xss参数设定。在Java虚拟机规范中描述了两种异常:

StackOverflowError :如果线程请求的栈深度超过了虚拟机所允许的最大深度,就会抛出该异常;

OutOfMemoryError:如果虚拟机在拓展栈的时候,无法申请到足够的空间,就会抛出该异常。

对于这两种异常来说,存在着一些互相重叠的地方:当栈空间无法继续分配的时候,到底是内存太小还是已使用的栈空间太大,其实只是对同一件事情的两种描述罢了。

当在单线程环境下,无论是由于栈帧太大还是虚拟机栈容量太小,当内存无法继续分配的时候,虚拟机抛出的都是StackOverflowError 异常。

在多线程环境下,如果为每个线程的栈分配的内存越大,反而越容易产生OOM异常。每个线程分配到的栈容量越大,可以建立的线程数量就自然减少了,那么在新建立线程的时候就很容易把内存耗尽,产生OOM异常。虚拟机默认参数栈深度大多数情况下能够达到1000~2000,对于正常的方法调用(包括递归)是完全够用的。但是,在多线程环境下的OOM,就只能通过减少最大堆和减少栈容量来换取更多的线程数量。

方法区和运行时常量池溢出

方法区用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。当前的一些主流框架,如Spring、Hibernate,对于类进行增强的时候都会使用到CGLib这类字节码技术,增强的类越多,就需要越大的方法区来保证动态生成Class可以加载入内存,这样的情况下可能会造成方法区的OOM异常。

在经常动态生成大量Class的应用中,需要特别注意类的回收状况。这类场景还在:大量JSP或动态生成JSP文件的应用(JSP第一次运行时需要编译为Java类)、基于OSGi的应用(即使是同一个类文件,被不同的加载器加载也会视作不同的类)等。

直接内存溢出

直接内存(DirectMemory)容量可以通过-XX : MaxDirectMemorySize指定,如果不指定,则默认与Java最大堆(-Xmx指定)一样。

由于直接内存(DirectMemory)导致的内存溢出,一个明显的特征是在Heap Dump文件中不会看见明显的异常,如果发现OOM之后Dump文件很小,而程序中又直接或间接使用了NIO,就可以考虑检查一下是否为直接内存(DirectMemory)溢出异常。

参考资料

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值