0717-7821348
业务指南

业务指南

您现在的位置: 首页 > 业务指南
OOM常见原因及解决方案
2019-11-26 22:14:55

当 JVM 内存严重缺乏时,就会抛出 java.lang.OutOfMemoryError 过错。本文总结了常见的 OOM 原因及其处理办法,如下图所示。如有遗失或过错,欢迎弥补纠正。



0x01、Java heap space

当堆内存(Heap Space)没有满足空间寄存新创立的目标时,就会抛出 java.lang.OutOfMemoryError:Javaheap space 过错(依据实践出产经历,能够对程序日志中的 OutOfMemoryError 装备关键字告警,一经发现,当即处理)。

原因剖析

Javaheap space 过错发作的常见原因能够分为以下几类:

1、恳求创立一个超大目标,一般是一个大数组。

2、超出预期的拜访量/数据量,一般是上游体系恳求流量飙升,常见于各类促销/秒杀活动,能够结合事务流量目标排查是否有尖状峰值。

3、过度运用完结器(Finalizer),该目标没有当即被 GC。

4、内存走漏(Memory Leak),很多目标引证没有开释,JVM 无法对其自动收回,常见于运用了 File 等资源没有收回。

处理方案

针对大部分状况,一般只需求经过 -Xmx 参数调高 JVM 堆内存空间即可。假如依然没有处理,能够参阅以下状况做进一步处理:

1、假如是超大目标,能够查看其合理性,比方是否一习次性查询了数据库悉数成果,而没有做成果数约束。

2、假如是事务峰值压力,能够考虑增加机器资源,或许做限流降级。

3、假如是内存走漏,需求找到持有的目标,修正代码规划,比方封闭没有开释的衔接。

0x02、GC overhead limit exceeded

当 Java 进程花费 98% 以上的时刻履行 GC,但只康复了不到 2% 的内存,且该动作接连重复了 5 次,就会抛出 java.lang.OutOfMemoryError:GC overhead limit exceeded 过错。简略地说,便是运用程序现已根本耗尽了一切可用内存, GC 也无法收回。

此类问题的原因与处理方案跟 Javaheap space 十分相似,能够参阅上文。

0x03、Permgen space

该过错表明永久代(Permanent Generation)已用满,一般是由于加载的 class 数目太多或体积太大。

原因剖析

永久代存储目标首要包含以下几类:

1、加载/缓存到内存中的 class 界说,包含类的称号,字段,办法和字节码;

2、常量池;

3、目标数组/类型数组所相关的 class;

4、JIT 编译器优化后的 class 信息。

PermGen 的运用量与加载到内存的 class 的数量/巨细正相关。

处理方案

依据 Permgen space 报错的机遇,能够选用不同的处理方案,如下所示:

1、程序发动报错,修正 -XX:MaxPermSize 发动参数,调大永久代空间。

2、运用重新布置时报错,很可能是没有运用没有重启,导致加载了多份 class 信息,只需重启 JVM 即可处理。

3、运行时报错,运用程序可能会动态创立很多 class,而这些 class 的生命周期很时间短,可是 JVM 默许不会卸载 class,能够设置 -XX:+CMSClassUnloadingEnabled 和 -XX:+UseConcMarkSweepGC这两个参数答应 JVM 卸载 class。

假如上述办法无法处理,能够经过 jmap 指令 dump 内存目标 jmap-dump:format=b,file=dump.hprof ,然后运用 Eclipse MAT https://www.eclipse.org/mat 功用逐个剖析开支最大的 classloader 和重复 class。

0x04、Metaspace

JDK 1.8 运用 Metaspace 替换了永久代(Permanent Generation),该过错表明 Metaspace 已被用满,一般是由于加载的 class 数目太多或体积太大。

此类问题的原因与处理办法跟 Permgenspace 十分相似,能够参阅上文。需求特别注意的是调整 Metaspace 空间巨细的发动参数为 -XX:MaxMetaspaceSize。

0x05、Unable to create new native thread

每个 Java 线程都需求占用必定的内存空间,当 JVM 向底层操作体系恳求创立一个新的 native 线程时,假如没有满足的资源分配就会报此类过错。

原因剖析

JVM 向 OS 恳求创立 native 线程失利,就会抛出 Unableto createnewnativethread,常见的原因包含以下几类:

1、线程数超越操作体系最大线程数 ulimit 约束;

2、线程数超越 kernel.pid_max(只能重启);

3、native 内存缺乏;

该问题发作的常见进程首要包含以下几步:

1、JVM 内部的运用程序恳求创立一个新的 Java 线程;

2、JVM native 办法代理了该次恳求,并向操作体系恳求创立一个 native 线程;

3、操作体系测验创立一个新的 native 线程,并为其分配内存;

4、假如操作体系的虚拟内存已耗尽,或是遭到 32 位进程的地址空间约束,操作体系就会回绝本次 native 内存分配;

5、JVM 将抛出 java.lang.OutOfMemoryError:Unableto createnewnativethread 过错。

处理方案

1、晋级装备,为机器供给更多的内存;

2、下降 Java Heap Space 巨细;

3、修正运用程序的线程走漏问题;

4、约束线程池巨细;

5、运用 -Xss 参数削减线程栈的巨细;

6、调高 OS 层面的线程最大数:履行 ulimia-a 查看最大线程数约束,运用 ulimit-u xxx 调整最大线程数约束。

ulimit -a .... 省掉部分内容 ..... max user processes (-u) 16384

0x06、Out of swap space?

该过错表明一切可用的虚拟内存已被耗尽。虚拟内存(Virtual Memory)由物理内存(Physical Memory)和交流空间(Swap Space)两部分组成。当运行时程序恳求的虚拟内存溢出时就会报 Outof swap space? 过错。

原因剖析

该过错呈现的常见原因包含以下几类:

1、地址空间缺乏;

2、物理内存已耗光;

3、运用程序的本地内存走漏(native leak),例如不断恳求本地内存,却不开释。

4、履行 jmap-histo:live 指令,强制履行 Full GC;假如几回履行后内存显着下降,则根本确以为 Direct ByteBuffer 问题。

处理方案

依据过错原因能够采纳如下处理方案:

1、晋级地址空间为 64 bit;

2、运用 Arthas 查看是否为 Inflater/Deflater 解压缩问题,假如是,则显式调用 end 办法。

3、Direct ByteBuffer 问题能够经过发动参数 -XX:MaxDirectMemorySize 调低阈值。

4、晋级服务器装备/阻隔布置,防止争用。

0x07、 Kill process or sacrifice child

有一种内核作业(Kernel Job)名为 Out of Memory Killer,它会在可用内存极低的状况下“杀死”(kill)某些进程。OOM Killer 会对一切进程进行打分,然后将评分较低的进程“杀死”,详细的评分规矩能够参阅 Surviving the Linux OOM Killer。

不同于其他的 OOM 过错, Killprocessorsacrifice child 过错不是由 JVM 层面触发的,而是由操作体系层面触发的。

原因剖析

默许状况下,Linux 内核答应进程恳求的内存总量大于体系可用内存,经过这种“错峰复用”的办法能够更有用的运用体系资源。

但是,这种办法也会无可防止地带来必定的“超卖”危险。例如某些进程继续占用体系内存,然后导致其他进程没有可用内存。此刻,体系将自动激活 OOM Killer,寻觅评分低的进程,并将其“杀死”,开释内存资源。

处理方案

1、晋级服务器装备/阻隔布置,防止争用。

2、OOM Killer 调优。

0x08、Requested array size exceeds VM limit

JVM 约束了数组的最大长度OOM常见原因及解决方案,该过错表明程序恳求创立的数组超越最大长度约束。

JVM 在为数组分配内存前,会查看要分配的数据结构在体系中是否可寻址,一般为 Integer.MAX_VALUE-2。

此类问题比较稀有,一般需求查看代码,承认事务是否需求创立如此大的数组,是否能够拆分为多个块,分批履行。

0x09、Direct buffer memory

Java 答应运用程序经过 Direct ByteBuffer 直接拜访堆外内存,许多高性能程序经过 DiOOM常见原因及解决方案rect ByteBuffer 结合内存映射文件(Memory Mapped File)完成高速 IO。

原因剖析

Direct ByteBuffer 的默许巨细为 64 MB,一旦运用超出约束,就会抛出 Directbuffer memory 过错。

处理方案

1、Java 只能经过 ByteBuffer.allocateDirect 办法运用 Direct ByteBuffer,因而,能够经过 Arthas 等在线确诊东西阻拦该办法进行排查。

2、查看是否直接或直接运用了 NIO,如 netty,jetty 等。

3、经过发动参数 -XX:MaxDirectMemorySize 调整 Direct OOM常见原因及解决方案ByteBuffer 的上限值。

4、查看 JVM 参数是否有 -XX:+DisableExplicitGC 选项,假如有就去掉,由于该参数会使 System.gc() 失效。

5、查看堆外内存运用代码,承认是否存在内存走漏;或许经过反射调用 sun.misc.Cleaner 的 clean() 办法来自动开释被 Direct ByteBuffer 持有的内存空间。

6、内存容量的确缺乏,晋级装备。

引荐东西&产品

JVM 内存剖析东西 mat

1、Eclipse Memory Analyzer

https://www.eclipse.org/mat

阿里云 APM 产品,支撑 OOM 反常关键字告警

2、ARMS

https://help.aliyun.com/document_detail/42966.html?spm=a2c4g.11174283.6.685.d69b668cuztvff

阿里 Java 在线确诊东西 Arthas(阿尔萨斯)

3、alibaba Arthas

https://github.com/alibaba/arthaOOM常见原因及解决方案s

参阅文章

1、Plumbr OutOfMemoryError(引荐,含代码示例)

2、GCeasy OutOfMemoryError

3、JVM 内存结构

欢迎在留言区留下你的观念,一同评论进步。假如今日的文章对你有所协助,让你有新的启示,有新的知道,欢迎转发共享给更多人。

来历:https://github.com/StabilityMan/StabilityGuide
作者:涯海