java OOM的种类整理
前言
java OOM的种类整理
1、分类
1.1【情况一】
|
|
这种是java堆内存不够,一个原因是真不够,另一个原因是程序中有死循环
如果是java堆内存不够的话,可以通过调整JVM下面的配置来解决:
-Xms3062m
-Xmx3062m
1.2【情况二】
|
|
【解释】
JDK6新增错误类型,当GC为释放很小空间占用大量时间时抛出;一般是因为堆太小,导致异常的原因,没有足够的内存。
【解决方案】
1、查看系统是否有使用大内存的代码或死循环;
2、通过添加JVM配置,来限制使用内存:
-XX:-UseGCOverheadLimit
1.3【情况三】
|
|
这种是P区内存不够,可通过调整JVM的配置:
-XX:MaxPermSize=128m
-XX:PermSize=128m
【注】
JVM的Perm区(永久代)主要用于存放Class和Meta信息的,Class在被Loader时就会被放到PermGen space。注意永久代是独立于新生代/年老代的第三个区域(存放类元数据),并不等同于存放对象实例的年老代;GC在主程序运行期间不会对Perm区进行清理,默认是64M大小,当程序需要加载的类比较多时,超过64M就会报这部分内存溢出了,需要加大内存分配,一般128m足够。
版本提示:JDK 8 起永久代已被移除,类元数据改存于本地内存的 Metaspace(受
‑XX:MaxMetaspaceSize限制,默认无上限),JDK 8+ 环境不会再出现PermGen space溢出,对应的是情况七。
1.4【情况四】
|
|
调整-XX:MaxDirectMemorySize= 参数,如添加JVM配置:
-XX:MaxDirectMemorySize=128m
1.5【情况五】
|
|
【原因】
Stack空间不足以创建额外的线程,要么是创建的线程过多,要么是Stack空间确实小了。
【解决】
JVM没有提供参数设置总的stack空间大小,但可以设置单个线程栈的大小;而32位系统的用户进程空间一共约3G(64位系统进程空间大得多,此问题主要困扰32位环境),除了Text/Data/BSS /MemoryMapping几个段之外,Heap和Stack空间的总量有限,是此消彼长的。因此遇到这个错误,可以通过两个途径解决:
1.通过 -Xss启动参数减少单个线程栈大小,这样便能开更多线程(当然不能太小,太小会出现StackOverflowError);
2.通过-Xms -Xmx 两参数减少Heap大小,将内存让给Stack(前提是保证Heap空间够用)。
1.6【情况六】
|
|
【原因】
这也内存溢出错误的一种,即线程栈的溢出,要么是方法调用层次过多(比如存在无限递归调用),要么是线程栈太小。
【解决】
优化程序设计,减少方法调用层次;调整-Xss参数增加线程栈大小。
1.7【情况七】
|
|
【原因】
JDK 8+ 使用 Metaspace 取代了永久代,类元数据存放在本地内存(Native Memory)中。出现该错误一般是因为动态生成类的场景过多(如大量反射、CGLIB 动态代理、Groovy/热部署),或没有限制 Metaspace 大小时物理内存被耗尽。
【解决】
通过 -XX:MaxMetaspaceSize 显式限制 Metaspace 大小避免拖垮物理内存(如 -XX:MaxMetaspaceSize=256m);同时排查是否有类加载器泄漏(重复部署的应用未卸载旧类)。注意 Metaspace 也可能伴随 OutOfMemoryError: Compressed class space(-XX:CompressedClassSpaceSize 可调)。
补充:JDK 8+ 还有
OutOfMemoryError: Out of swap space(物理内存+交换空间耗尽,需从操作系统层面排查)与Requested array size exceeds VM limit(数组长度超过 VM 上限,通常是程序 bug 而非调优问题)两种较少见的形态。
2、JDK 21 时代的形态对照
| OOM | JDK 7/8 时代的处理 | JDK 21 时代的变化 |
|---|---|---|
Java heap space |
调大 Xms/Xmx | 处理思路不变;G1/ZGC 下优先看堆转储找泄漏源 |
GC overhead limit exceeded |
关 -XX:-UseGCOverheadLimit(不建议) |
该错误不再出现:它是 Parallel GC 专属的兜底检查,G1/ZGC 没有此机制,直接按 heap 泄漏排查 |
PermGen space |
调 MaxPermSize | 已随永久代消失,对应变为 Metaspace(见情况七) |
Direct buffer memory |
调 -XX:MaxDirectMemorySize |
不变,Netty/堆外缓存场景常见 |
unable to create new native thread |
减 Xss 或减堆腾内存 | 形态大变:JDK 21 虚拟线程(Executors.newVirtualThreadPerTaskExecutor())不 1:1 占用内核线程,百万级任务不再是问题;仅当载体平台线程或进程级线程数真打满时才报此错 |
Metaspace |
——(当时不存在) | 新增形态,见情况七 |
一句话:调参三板斧(PermSize/CMS/GC overhead 开关)在 JDK 21 上大多已失效或无意义,OOM 排查的重心从「调 JVM 参数」转向「堆转储分析 + 泄漏定位 + 虚拟线程改造」。
相关阅读
- 原文作者:Anttu
- 原文链接:https://anTtutu.github.io/post/2018-05-08-oom/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。