《100个“反常识”经验04:JVM OOM但内存还有剩余?》
·
📖 先唠两句
大家好啊,我是阿垚。
欢迎来到《100个“反常识”经验》第04期。
上期聊了服务器时间跳变导致证书失效。今天聊一个Java程序员必踩的坑:
JVM报OOM(OutOfMemoryError),但用free命令一看,物理内存还有剩余。
你是不是也遇到过?明明服务器还有好几个G的空闲内存,Java进程却说自己内存不够了。
这到底是怎么回事?今天一次讲清楚。💀
🌙 一个让人抓狂的下午
那天下午,生产环境报警:Java服务OOM了。
我赶紧登录服务器:
bash
free -h
输出:
bash
total used free Mem: 15G 10G 5G
还有5G空闲内存呢,怎么就OOM了?
又查了Java进程:
bash
ps aux | grep java
RES显示用了4G,XMX设置的是8G。
8G没到,物理内存还剩5G,怎么就OOM了?(◐‿◑)
折腾了半天,最后发现问题出在哪?
堆外内存(Direct Memory)把虚拟地址空间耗尽了。
🔧 为什么会出现这种情况?
Java进程的XMX只限制了堆内存(Heap),但以下区域不受XMX控制:
| 内存区域 | 说明 | 常见来源 |
|---|---|---|
| 直接内存 | DirectByteBuffer | Netty、NIO |
| 本地内存 | Native Code | JNI调用、第三方库 |
| 线程栈 | Thread Stack | 线程数过多 |
| Metaspace | 类元数据 | 动态加载类 |
| CodeCache | JIT编译代码 | 热点方法过多 |
这些区域加起来,很容易超过物理内存限制,导致OOM。
📝 排错三步法
第一步:检查OOM类型
bash
grep -i "OutOfMemoryError" /var/log/app.log
| OOM类型 | 含义 | 排查方向 |
|---|---|---|
| Java heap space | 堆内存不足 | 增加XMX,检查内存泄漏 |
| Direct buffer memory | 堆外内存不足 | 限制DirectMemory,检查Netty |
| Metaspace | 元空间不足 | 增加MaxMetaspaceSize |
| Unable to create new native thread | 线程数超限 | 检查线程泄漏 |
第二步:查看堆外内存使用
bash
jcmd <pid> VM.native_memory summary
关注 Internal、Direct、Native 等区域的数值。
第三步:检查内存映射数量
bash
cat /proc/<pid>/maps | wc -l
正常值几千,超过几万说明可能有内存泄漏。
💡 永久防范方案
✅ 设置堆外内存上限
bash
-XX:MaxDirectMemorySize=2G
✅ 开启NMT跟踪内存
bash
-XX:NativeMemoryTracking=detail
✅ 定期对比内存快照
bash
jcmd <pid> VM.native_memory baseline jcmd <pid> VM.native_memory summary.diff
✅ 升级Netty等框架版本
旧版本Direct Memory释放有问题。
🔜 下期预告
《100个“反常识”经验05:能ping通但curl不通?》
评论区分享你遇到过最诡异的OOM问题。
——阿垚,一个踩过2万次坑的“老”IT
更多推荐




所有评论(0)