遇到OOM如何处理?

本文详细探讨了Java内存管理和垃圾收集,包括不同内存区域的作用、GC线程、Heap大小设置及其对性能的影响。重点讲解了如何避免OOM,如合理设置-Xms、-Xmx、-Xmn参数,并介绍了不同GC类型及其适用场景。此外,还提到了服务器环境如Tomcat、WebLogic和Eclipse等的JVM内存配置方法,以及GC性能指标和优化策略。
摘要由CSDN通过智能技术生成
首先,要搞清OOM的分类:
OMM主要三类: permgen OOM , heap OOM, stack overflow 
permgen OOM : 这个主要是由于加载的类太多,或者反射的类太多, 还有 调用 String.intend(jdk7之前)也会造成这个问题。所以出现了这个问题,就检查这三个方面;
heap OOM : 基本是按照 1楼的方式就可以解决了,主要是因为一些无用对象没有及时释放造成的,检查代码加上 heap dump 去分析吧
stack overflow : 这个主要是由于调用层数,或者递归深度太大造成的,看异常信息,基本上就能定位得出来了

把内存大户找出来。
1. 可通过命令定期抓取heap dump 或者 启动参数OOM时 自动抓取heap dump
2. 通过对比多个heap dump,以及heap dump的内容,找出内存大户
3. 分析占用的内存对象,是否是因为错误导致的内存未及时释放,或者 数据过多导致的内存溢出
4. 根据原因,fix bug 或者 修改方案


堆/Heap 
JVM管理的内存叫堆;在32Bit操作系统上有4G的限制,一般来说Windows下为2G,而Linux 下为3G;64Bit的就没有这个限制。 
JVM初始分配的内存由-Xms指定,默认是物理内存的1/64但小于1G。 
JVM最大分配的内存由-Xmx指定,默认是物理内存的1/4但小于1G。 
默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制,可以由 -XX:MinHeapFreeRatio=指定。 
默认空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制,可以由 -XX:MaxHeapFreeRatio=指定。 
服务器一般设置-Xms、-Xmx相等以避免在每次GC后调整堆的大小,所以上面的两个参数没啥用。 

分代/堆模型 
分代是Java垃圾收集的一大亮点,根据对象的生命周期长短,把堆分为3个代:Young,Old和Permanent,根据不同代的特点采用不同的收集算法,可以扬长避短。可参考如下的模型图: 

GC的类型 
当每个代满了之后都会自动促发collection,各收集器触发的条件不一样,当然也可以通过一些参数进行强制设定。主要分为两种类型: 
Minor Collection:GC用较高的频率对young进行扫描和回收,采用复制算法。 
Major Collection:同时对Young和Old进行内存收集,也叫Full GC;因为成本关系对Old的检查回收频率要比Young低很多,采用标记清除/标记整理算法。可以通过调用代码System.gc()引发major collection,使用-XX:+DisableExplicitGC禁止它,或设为CMS并发 -XX:+ExplicitGCInvokesConcurrent。 
更为具体的阐述如下
由于年轻代进进出出的人多而频繁,所以年轻代的GC也就频繁一点,但涉及范围也就年轻代这点弹丸之地内的对象,其特点就是少量,多次,但快速,称之为 Minor Collection。当年轻代的内存使用达到一定的阀值时,Minor Collection就被触发,Eden及某一Survior space(from space)之内存活的的对象被移到另一个空的Survior space(to space)中,然后from space和to space角色对调。当一个对象在两个survivor space之间移动过一定次数(达到预设的阀值)时,它就足够old了,够资格呆在年老代了。当然,如果survivor space比较小不足以容下所有live objects时,部分live objects也会直接晋升到年老代。
Survior spaces可以看作是Eden和年老代之间的缓冲,通过该缓冲可以检验一个对象生命周期是否足够的长,因为某些对象虽然逃过了一次Minor Collection,并不能说明其生命周期足够长,说不定在下一次Minor Collection之前就挂了。这样一定程度上确保了进入年老代的对象是货真价实的,减少了年老代空间使用的增长速度,也就降低年老代GC的频率。
当年老代或者永久代的内存使用达到一定阀值时,一次基于所有代的GC就触发了,其特定是涉及范围广(量大),耗费的时间相对较长(较慢),但是频率比较低(次数少),称之为Major Collection(Full Collection)。通常,首先使用针对年轻代的GC算法进行年轻代的GC,然后使用针对年老代的GC算法对年老代和永久代进行GC。 


调优总结
年轻代大小选择
响应时间优先的应用:尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择)。在此种情况下,年轻代收集发生的频率也是最小的。同时,减少到达年老代的对象。
吞吐量优先的应用:尽可能的设置大,可能到达Gbit的程度。因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8CPU以上的应用。
年老代大小选择
响应时间优先的应用:年老代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数。如果堆设置小了,可以会造成内存碎片、高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间。最优化的方案,一般需要参考以下数据获得:
  1. 并发垃圾收集信息  
  2. 持久代并发收集次数  
  3.  传统GC信息  
  4.  花在年轻代和年老代回收上的时间比例  
遇到Metaspace的OOM异常时,可以尝试以下排查和处理方法: 1. 增加Metaspace内存空间: - 通过调整JVM参数来增加Metaspace的内存空间,可以使用`-XX:MetaspaceSize`和`-XX:MaxMetaspaceSize`参数来设置初始大小和最大大小。 - 适当增加Metaspace的内存空间可以缓解OOM异常,但需要注意不要过度分配内存导致其他部分受影响。 2. 检查是否存在大量动态生成的类: - 使用CGLIB、反射等方式动态生成类会消耗Metaspace的内存。 - 检查代码中是否存在频繁动态生成类的逻辑,考虑优化或减少动态生成类的使用。 3. 检查字符串常量的使用: - Metaspace也存储字符串常量,过多的字符串常量会增加Metaspace的内存压力。 - 检查代码中是否存在大量重复的字符串常量,可以使用字符串常量池或intern()方法来避免重复创建字符串对象。 4. 分析堆栈信息和内存快照: - OOM异常发生时,记录相关堆栈信息和内存快照。 - 使用工具(如MAT、VisualVM等)分析堆栈信息和内存快照,找出可能存在的内存泄漏问题。 5. 优化代码和资源管理: - 检查代码中是否存在不必要的类加载、资源加载或资源未释放的情况。 - 优化代码逻辑,避免重复加载和使用不必要的资源。 6. 升级JDK版本: - Metaspace的实现在不同的JDK版本中可能会有差异,升级到较新的JDK版本可能会提供更好的Metaspace管理和性能。 7. 参考官方文档和社区资源: - 参考Java官方文档、Oracle官方文档以及相关社区资源,了解Metaspace的工作原理和最佳实践。 - 在问题解决过程中,可以参考其他开发者的经验分享和解决方案。 重要的是,处理Metaspace的OOM异常需要综合考虑代码、配置和环境等多个因素,根据具体情况采取相应的措施。如果问题持续存在或难以解决,可以考虑寻求专业的Java性能优化工具或咨询服务。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值