Ubuntu Java 日志出现 OOM 的定位与处置
一、先判断 OOM 来源
二、快速定位步骤
sudo grep -i "out of memory" /var/log/kern.log 或 journalctl -k | grep -i "oom",若出现 “Kill process … (java)” 基本可判定为内核 OOM。grep -i "OutOfMemoryError" app.log;结合异常类型决定后续分析路径(堆、元空间、直接内存、线程、GC 开销等)。-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/dumps/heap.hprof-Xlog:gc*,gc+heap=debug:file=/data/logs/gc.log:time,tags:filecount=10,filesize=100Mjps -l 查 PIDjstat -gc <PID> 1000 观察各区使用与 GC 行为jmap -dump:format=b,file=heap.hprof <PID> 手动导出堆转储jstack <PID> > threads.txt 导出线程栈ulimit -u 检查是否触及系统线程/进程数限制。三、常见 OOM 类型与对策
| 错误类型 | 典型特征 | 优先检查 | 处理建议 |
|---|---|---|---|
| Java heap space | 堆分配失败 | GC 日志、堆转储 | 排查泄漏与大对象;合理设置 -Xms/-Xmx;必要时优化数据结构/批处理 |
| Metaspace | 类加载过多 | 类加载数、ClassLoader 泄漏 | 控制动态类生成;设置 -XX:MaxMetaspaceSize;修复热部署泄漏 |
| Direct buffer memory | NIO/Netty 直接内存不足 | 直接缓冲区使用、回收 | 释放 ByteBuffer 引用;必要时调 -XX:MaxDirectMemorySize;审视第三方库 |
| unable to create new native thread | 线程创建失败 | 线程数、栈大小、ulimit | 使用线程池限流;减小 -Xss;提升 ulimit -u 与内核限制 |
| GC overhead limit exceeded | GC 占时高、回收少 | GC 日志 | 多半是堆被占满或泄漏;先定位根因,再调堆与 GC 策略 |
| Requested array size exceeds VM limit | 超大数组分配 | 数组长度计算 | 修正长度逻辑,避免接近 Integer.MAX_VALUE 的分配 |
| CodeCache | JIT 代码缓存满 | 编译日志、热点方法 | 调大 -XX:ReservedCodeCacheSize 或优化编译策略 |
| Out of swap space? | 虚拟内存耗尽 | 物理内存、swap、native 内存 | 增加内存或 swap;排查 native/堆外泄漏;降低堆外占用 |
| Kernel OOM Killer | 内核日志杀 java | 内存与 swap、overcommit | 控制内存申请、加内存/扩 swap、优化负载或限流降级 |
说明:上表覆盖了生产最常见场景;不同 JDK 版本与 GC 策略下表现略有差异,需结合日志与现场证据综合判断。
四、系统层面的优化与注意事项
sudo swapoff -asudo dd if=/dev/zero of=/swapfile bs=1M count=8192sudo mkswap /swapfilesudo swapon /swapfile五、最小可用的应急与根治方案