当前位置: 首页 > news >正文

JVM中产生OOM(内存溢出)的8种典型情况及解决方案

Java中的OutOfMemoryError(OOM)是当JVM内存不足时抛出的错误。本文将全面剖析JVM中产生OOM的各种情况,包括堆内存溢出、方法区溢出、栈溢出等,并提供详细的诊断方法和解决方案。

一、OOM基础概念


1.1 OOM错误类型

  • Java中的OOM是java.lang.OutOfMemoryError的子类,常见的有:
  • Java heap space:堆空间不足
  • GC Overhead limit exceeded:GC效率低下
  • PermGen space/Metaspace:方法区溢出
  • Unable to create new native thread:线程创建失败
  • Requested array size exceeds VM limit:数组过大
  • Direct buffer memory:直接内存溢出
  • Code cache:代码缓存区满
  • Kill process or sacrifice child:Linux系统级限制

二、堆内存溢出(Java heap space)


2.1 产生原因


当对象需要分配到堆内存时,如果堆内存不足且无法通过GC回收足够空间时抛出。

// 典型示例

public class HeapOOM {public static void main(String[] args) {List<Object> list = new ArrayList<>();while(true) {list.add(new byte[1024*1024]); // 每次分配1MB}}
}


2.2 错误信息

java.lang.OutOfMemoryError: Java heap space


2.3 解决方案


调整堆大小:

-Xms256m -Xmx1024m  # 初始堆256MB,最大堆1GB



内存分析:

使用jmap获取堆转储:

jmap -dump:format=b,file=heap.hprof <pid>


使用MAT/Eclipse Memory Analyzer分析


代码优化:

避免内存泄漏(如静态集合、未关闭资源)
使用对象池重用对象


三、GC开销超限(GC Overhead limit exceeded)


3.1 产生原因


当JVM花费超过98%的时间进行GC,但只恢复了不到2%的堆空间时抛出。

// 典型场景:创建大量生命周期短的对象

public class GCOverheadOOM {public static void main(String[] args) {Map<Key, String> map = new HashMap<>();while(true) {for(int i=0; i<10000; i++) {map.put(new Key(i), "Value"+i);}map.clear(); // 不完全清除}}
}


3.2 错误信息

java.lang.OutOfMemoryError: GC Overhead limit exceeded


3.3 解决方案

  1. 增加堆大小:
    ​
    -Xmx2g -XX:+UseG1GC​

  2. 优化GC策略:
  • 对于大量短生命周期对象,使用G1或ZGC
  • 调整新生代大小:
  • -XX:NewRatio=2  # 新生代占堆的1/3

  1. 代码改进:
  • 减少临时对象创建
  • 使用更高效的数据结构

四、方法区溢出(Metaspace/PermGen)


4.1 产生原因


JDK8前称为PermGen space,JDK8+称为Metaspace,存储类元数据信息。

// 通过动态生成类填满方法区
public class MetaspaceOOM {static class OOMObject {}public static void main(String[] args) {while(true) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(OOMObject.class);enhancer.setUseCache(false);enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> methodProxy.invokeSuper(o, objects));enhancer.create(); // 动态创建类}}
}


4.2 错误信息
 

// JDK7及之前
java.lang.OutOfMemoryError: PermGen space// JDK8+
java.lang.OutOfMemoryError: Metaspace


4.3 解决方案


调整Metaspace大小:

-XX:MaxMetaspaceSize=256m


JDK8前调整PermGen:

-XX:MaxPermSize=128m


减少动态类生成:

缓存动态代理类
限制反射使用


五、线程栈溢出(Unable to create new native thread)


5.1 产生原因


当创建线程数量超过系统限制时发生。

public class ThreadOOM {public static void main(String[] args) {while(true) {new Thread(() -> {try { Thread.sleep(100000); } catch(InterruptedException e) {}}).start();}}
}


5.2 错误信息

java.lang.OutOfMemoryError: Unable to create new native thread


5.3 解决方案


减少线程数量:

使用线程池:

ExecutorService pool = Executors.newFixedThreadPool(100);

调整系统限制:

ulimit -u  # 查看最大线程数
ulimit -u 2048  # 设置最大线程数



减少栈大小:

-Xss256k  # 默认1MB,减少可创建更多线程



六、直接内存溢出(Direct buffer memory)


6.1 产生原因


NIO使用的直接内存(堆外内存)不足时抛出。

public class DirectMemoryOOM {public static void main(String[] args) {// 绕过DirectByteBuffer限制,直接分配内存List<ByteBuffer> buffers = new ArrayList<>();while(true) {buffers.add(ByteBuffer.allocateDirect(1024*1024)); // 1MB}}
}


6.2 错误信息

java.lang.OutOfMemoryError: Direct buffer memory



6.3 解决方案


调整直接内存大小:

-XX:MaxDirectMemorySize=256m


显式回收:

((DirectBuffer)buffer).cleaner().clean();


使用池化技术:

Netty的ByteBuf池


七、数组过大溢出(Requested array size exceeds VM limit)


7.1 产生原因


尝试分配超过JVM限制的数组。

public class ArraySizeOOM {public static void main(String[] args) {int[] arr = new int[Integer.MAX_VALUE]; // 约2^31-1个元素}
}



7.2 错误信息

java.lang.OutOfMemoryError: Requested array size exceeds VM limit


7.3 解决方案
减小数组大小:

分块处理大数据
使用集合替代:

List<Integer> list = new ArrayList<>();


调整数据结构:

使用数据库或文件存储


八、代码缓存溢出(Code cache)


8.1 产生原因


JIT编译的代码填满代码缓存区。

// 通常由大量方法被JIT编译导致

public class CodeCacheOOM {public static void main(String[] args) {// 需要大量方法编译的代码}
}



8.2 错误信息

java.lang.OutOfMemoryError: Code cache



8.3 解决方案
增加代码缓存大小:

-XX:ReservedCodeCacheSize=256m



减少编译阈值:

-XX:CompileThreshold=10000



关闭分层编译:

-XX:-TieredCompilation



九、系统级OOM(Kill process or sacrifice child)


9.1 产生原因
Linux系统的OOM Killer终止进程。

dmesg | grep -i kill


输出示例:

Out of memory: Kill process 12345 (java) score 999 or sacrifice child



9.2 解决方案
增加系统内存
调整OOM Killer策略:

echo -17 > /proc/[pid]/oom_adj


限制容器内存(Docker):

docker run -m 2g my-java-app



十、OOM诊断工具链

OOM诊断工具链
工具用途示例命令
jstat监控内存和GCjstat -gcutil <pid> 1000
jmap堆转储jmap -dump:live,format=b,file=heap.hprof <pid>
jvisualvm可视化分析图形化界面
MAT内存分析分析hprof文件
jcmd多功能工具jcmd <pid> VM.native_memory


十一、OOM预防最佳实践


代码层面:

避免内存泄漏(监听器、静态集合)
及时关闭资源(数据库连接、文件流)
使用WeakReference处理缓存
JVM配置:

# 基础配置示例

-Xms1g -Xmx2g -XX:MaxMetaspaceSize=256m 
-XX:+UseG1GC -XX:MaxGCPauseMillis=200


监控预警:

JMX监控堆内存使用
Prometheus + Grafana监控体系
设置合理的GC日志监控:

-Xlog:gc*:file=gc.log:time:filecount=5,filesize=10M



十二、总结


OOM类型与对应解决方案速查表:

OOM类型相关内存区域典型解决方案
Java heap space增大堆,修复内存泄漏
GC Overhead优化GC策略,减少对象创建
Metaspace/PermGen方法区增大Metaspace,减少动态类生成
Unable to create thread减少线程数,调整-Xss
Direct buffer直接内存增大MaxDirectMemorySize,显式回收
Array size减小数组尺寸,分块处理
Code cacheJIT代码缓存增大ReservedCodeCacheSize
System OOM系统内存增加物理内存,调整OOM Killer

http://www.dtcms.com/a/362767.html

相关文章:

  • 自从不小心踢了一脚主机之后,电脑频繁蓝屏、死机、无法开机……
  • 鸿蒙Next开发指南:XComponent与Progress组件的深度解析与实践
  • 睿思芯科正式加入龙蜥社区,携手共建 RISC-V 服务器生态新标杆
  • react+taro的使用整理
  • 【JavaEE】(21)Spring AOP
  • 解密GTH时钟架构:一网打尽收发器时钟之谜
  • 火语言 RPA 界面应用生成:低代码逻辑下的功能设计与场景适配
  • PowerPoint和WPS演示如何循环放映PPT
  • 想找Gamma的平替?这几款AI PPT工具值得试试
  • 从技术架构到经济价值:低代码在企业开发中的成本节约潜力
  • LeetCode 925.长按键入
  • 哈希表-面试题01.02.判定是否互为字符重排-力扣(LeetCode)
  • 趣味学RUST基础篇(HashMap)
  • 二叉树的非递归遍历 | 秋招面试必备
  • Spring Bean
  • LLM面试50问:NLP/RAG/部署/对齐/安全/多模态全覆盖
  • R语言根据经纬度获得对应样本的省份
  • WPF依赖属性和依赖属性的包装器:
  • iOS混淆工具实战 视频流媒体类 App 的版权与播放安全保护
  • 安卓学习 之 gradle下载失败的解决方法
  • Elasticsearch面试精讲 Day 5:倒排索引原理与实现
  • 跨越产业技术障碍、创新制造模式的智慧工业开源了
  • 【开题答辩全过程】以宠物生活社区为例,包含答辩的问题和答案
  • 扩散模型驱动的智能设计与制造:下一场工业革命?
  • 最新!阿里财报电话会蒋凡与吴泳铭透露重要信息:淘宝闪购成绩斐然;零售与AI双轮驱动;阿里云推出“Agent Bay”新产品···
  • 物联网为何离不开天硕工业级SSD固态硬盘?
  • maven 常用指令
  • Corona渲染噪点终结指南:3ds Max高效去噪全攻略
  • 【3D 入门-3】常见 3D 格式对比,.glb / .obj / .stl / .ply
  • 通信中FDD和TDD的区别