目录
一、先区分:是系统内存满,还是应用进程内存满
1. 查看整机内存(Linux)
2. 定位占用最高的进程
二、常见原因分类 & 对应排查
场景 1:Linux 系统层面内存高(非应用泄漏)
原因 1:Linux 文件缓存(buff/cache)占用高
原因 2:僵尸进程 / 孤儿进程、残留后台进程
原因 3:系统内核、驱动、定时任务异常
场景 2:中间件 / 数据库占用内存过高(高频)
1. MySQL
2. Redis
3. Elasticsearch
场景 3:Java 应用内存爆满(最主流业务场景)
常见根因
分步排查命令(Java)
Java 快速解决
场景 4:Docker / K8s 容器内存满
三、标准处理流程(生产上线直接套用)
第一步:紧急止血(内存 95%+,业务即将宕机)
第二步:定位根因(必做,防止复发)
第三步:根治优化(长期方案)
四、快速自查清单(一分钟自检)
先分系统层、应用层、JVM / 容器层三大类定位问题,再按「紧急止血→根因排查→根治优化」处理,覆盖 Linux 服务器、Java 应用、容器 (K8s/Docker) 主流场景。
一、先区分:是系统内存满,还是应用进程内存满
登录服务器执行基础命令,第一步定性:
1. 查看整机内存(Linux)
free -htotal:总内存used:已使用内存buff/cache:页缓存、文件缓存(可回收,不算真正泄漏)available:真实可用内存(核心指标)
关键判断:
- available 很低:整机内存真吃满,业务 / 系统占用过高
used高但available充足:只是系统缓存多,无需处理
2. 定位占用最高的进程
# 按内存排序,看TOP进程 top -o %MEM # 或简洁版 ps aux --sort=-%mem | head -10记下PID、进程名(Java/Go/ 数据库 / 中间件等)。
二、常见原因分类 & 对应排查
场景 1:Linux 系统层面内存高(非应用泄漏)
原因 1:Linux 文件缓存(buff/cache)占用高
Linux 会尽量利用空闲内存做磁盘 IO 缓存,读写文件越多缓存越大,表象内存 90%+,但available正常。
- 特征:业务无卡顿、无 OOM、无服务宕机,负载平稳
- 临时清理(仅应急,不建议频繁操作):
# 清理页缓存(生产慎用,会短暂IO抖动) echo 1 > /proc/sys/vm/drop_caches - 根治:无需根治,这是 Linux 正常机制,内存本就该被利用。
原因 2:僵尸进程 / 孤儿进程、残留后台进程
大量失效进程占用堆内存、句柄。 排查:
ps -ef | grep defunct # 查看僵尸进程处理:找到父进程重启 / 杀掉,清理残留进程。
原因 3:系统内核、驱动、定时任务异常
crontab 疯狂执行脚本、日志轮转异常、内核 BUG、挂载大文件等。 排查:
crontab -l # 查看定时任务 df -h && du -sh / # 检查磁盘、大文件 dmesg -T # 查看内核报错、OOM killer日志场景 2:中间件 / 数据库占用内存过高(高频)
生产最常见:MySQL、Redis、Elasticsearch、MQ、Nginx 等中间件吃满内存。
1. MySQL
- 原因:
innodb_buffer_pool_size设置过大,超过服务器物理内存;连接数过多、慢查询、临时表 / 排序区暴涨。 - 排查:
show engine innodb status;、查看连接数、慢日志 - 解决:
- buffer_pool 建议设为物理内存 50%~70%(单机 MySQL),集群下调低
- 优化慢 SQL、索引、大事务,限制最大连接数
2. Redis
- 原因:
maxmemory未配置、海量大 Key、热 Key、持久化 (RDB/AOF) fork 子进程占用内存、内存淘汰策略失效。 - 排查:
info memory、redis-cli --bigkeys - 解决:
- 配置
maxmemory+ 合理淘汰策略(allkeys-lru) - 拆分大 Key、过期无用数据、限制客户端连接
- 配置
3. Elasticsearch
- 原因:JVM 堆设置过大、分片过多、查询聚合语句炸内存、内存锁定开启。
- 解决:ES 堆不超过 31G,优化 DSL 语句、合理规划分片。
场景 3:Java 应用内存爆满(最主流业务场景)
进程为java/javac,内存持续上涨、触发 OOM、GC 频繁。
常见根因
- 内存泄漏(代码问题,内存只涨不跌)
- JVM 堆参数设置不合理(堆太大 / 太小)
- 堆外内存泄漏(Netty、NIO、JNI、第三方 SDK)
- 频繁创建大对象、大集合(接口批量查数据不分页)
- 线程数爆炸、线程栈溢出
分步排查命令(Java)
- 查看 JVM 整体状态
jstat -gc PID 1000 # 每秒打印GC情况,看FGC次数、堆使用率- 频繁 Full GC、堆内存使用率逼近最大值 → 堆溢出 / 泄漏
- 导出堆快照(生产优先在线排查,再 dump)
# 生成堆dump(大文件,选业务低峰) jmap -dump:format=b,file=heap.hprof PID用MAT/VisualVM分析:
- 查找大对象、死集合、静态集合无限持有对象
- 定位泄漏类、泄漏代码行
- 排查堆外内存(Netty 应用重点)
# 查看进程虚拟内存、物理内存 cat /proc/PID/status | grep Vm堆外高:检查 Netty 内存池、第三方 SDK、文件流未关闭。
Java 快速解决
- 临时应急:重启应用(最快降内存,治标)
- 调整 JVM 参数(根据服务器内存合理配置) 示例(8C16G 服务器,单机 Java 应用):
-Xms8g -Xmx8g # 堆固定大小,避免扩容抖动 -XX:MaxDirectMemorySize=2g # 限制堆外内存 - 代码修复:
- 集合使用后清空、避免静态 List/Map 无限累加
- 接口分页,禁止全表查询
- 关闭流、连接、上下文(try-with-resources)
- 修复循环引用、匿名内部类持有外部对象
场景 4:Docker / K8s 容器内存满
容器内内存 95%+,分为容器限制不足和容器内应用泄漏。
- 查看容器内存限制与使用
docker stats # 进入容器内部排查 docker exec -it 容器ID free -h- 原因:
- 容器
memory limit设置过小(业务量上涨后不够用) - 容器内应用本身内存泄漏(同 Java / 业务代码问题)
- 容器日志打满、日志文件不轮转
- 容器
- 解决:
- 合理调高容器内存配额
- 配置容器日志轮转,避免日志占内存 / 磁盘
- 进容器内部,按上面「应用排查」定位代码问题
三、标准处理流程(生产上线直接套用)
第一步:紧急止血(内存 95%+,业务即将宕机)
- 先看
free -h+dmesg,确认是否被系统 OOM 杀进程 - 若应用进程占比最高:低峰重启对应服务,快速释放内存
- 若缓存过高:按需执行缓存清理(业务低峰操作)
- 临时扩容服务器 / 容器内存(临时扛流量)
第二步:定位根因(必做,防止复发)
- 区分:系统缓存 / 中间件 / 业务应用 / 容器
- 应用类:抓 GC 日志、堆 dump、线程栈,分析泄漏点
- 中间件类:检查配置、慢查询、大 Key、连接数
- 系统类:检查定时任务、大文件、内核日志
第三步:根治优化(长期方案)
- 配置优化
- JVM / 中间件内存参数合理化,不超物理内存
- 容器资源配额、日志轮转、连接池上限收紧
- 代码优化
- 修复内存泄漏、批量接口分页、关闭资源句柄
- 优化大对象、循环逻辑、静态集合
- 架构优化
- 大服务拆分为微服务,分散内存压力
- 热点数据做缓存、异步化,减少内存常驻
- 监控告警
- 配置内存使用率告警(阈值建议 80% 预警,90% 紧急)
- 监控 GC、OOM、进程状态,提前发现问题
四、快速自查清单(一分钟自检)
free -h→ available 是否充足?top→ 哪个进程吃内存最多?dmesg→ 有没有Out of memory: Killed process?- Java 应用:
jstat -gc→ 是否频繁 Full GC? - 中间件:Redis/MySQL/ES 内存配置是否超限?
- 容器:是否配置了内存限制、日志是否爆了?