📖 先唠两句

大家好啊,我是阿垚。

欢迎来到《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

关注 InternalDirectNative 等区域的数值。

第三步:检查内存映射数量

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

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐