以下是针对 Linux 系统内存使用率高的分步排查方法,结合用户进程占用、tmpfs 内存占用、内核内存泄漏和黑洞内存等特殊情况进行分析:
第一步:初步观察系统整体内存使用情况
1. 查看系统内存概况
- 命令:
free -h
或cat /proc/meminfo
- 关键指标:
MemTotal
:系统总内存。MemFree
:空闲内存。Buffers/Cached
:缓冲/缓存内存(可被回收)。Swap
:交换空间使用情况(若启用)。
- 判断逻辑:
- 若
MemFree
较低但Buffers/Cached
较高,可能是缓存占用较多,不一定是真实内存不足。 - 若
Swap
使用量显著增加,说明物理内存严重不足,需重点排查。
- 若
- 关键指标:
2. 确认内存使用率计算方式
- 内存使用率公式:
内存使用率 = (MemTotal - MemFree - Buffers - Cached) / MemTotal * 100%
- 若计算结果高,需进一步分析各组件占用。
第二步:排查用户进程内存占用
1. 找出高内存占用的用户进程
- 命令:
top
或htop
(动态排序,按M
键以内存占用排序)。ps aux --sort=-rss | head -n 10
(静态排序,查看 RSS 内存占用前 10 的进程)。pmap <PID>
:查看进程的内存映射详情(如堆、栈、共享库等)。
- 重点分析:
- 是否有异常进程(如僵尸进程、内存泄漏的应用)。
- 进程是否正常(如数据库、Java 应用等本身内存占用较高是否合理)。
2. 排查进程内存泄漏
- 工具:
- Valgrind(用户态程序):检测堆内存泄漏(适用于 C/C++ 程序)。
- Java 工具:
jmap
、jconsole
、VisualVM
(分析 Java 进程的堆内存使用)。 - Python 工具:
tracemalloc
(跟踪 Python 程序的内存分配)。
- 方法:
- 观察进程内存是否随时间持续增长(无下降趋势)。
- 对比进程的虚拟内存(VSS)和驻留内存(RSS),若 VSS 远大于 RSS,可能存在内存碎片化。
第三步:排查 tmpfs 内存占用
1. 确认 tmpfs 的使用情况
- tmpfs 简介:基于内存的文件系统(如
/dev/shm
),占用的内存会计入MemUsed
。 - 命令:
df -hT | grep tmpfs
:查看 tmpfs 挂载点及其容量、使用量。du -sh /dev/shm/*
:查看/dev/shm
下文件/目录的内存占用。
- 常见问题:
- 应用是否在
/dev/shm
中生成大文件(如数据库临时文件、日志)。 - 是否存在未清理的临时文件(如进程崩溃后残留的文件)。
- 应用是否在
2. 清理 tmpfs 内存
- 临时清理:删除
/dev/shm
下的无用文件。 - 长期优化:
- 调整 tmpfs 挂载大小(如
mount -o size=2G /dev/shm
)。 - 避免在 tmpfs 中存储持久化数据。
- 调整 tmpfs 挂载大小(如
第四步:排查内核内存泄漏
1. 检查 Slab 内存泄漏
- Slab 内存:内核通过 Slab 分配器管理的对象(如 inode、dentry 等)。
- 命令:
cat /proc/meminfo | grep SUnreclaim
:查看不可回收的 Slab 内存(SUnreclaim
)是否持续增长。cat /proc/slabinfo | sort -k3 -n
:按对象数量排序,观察是否有异常增长的 Slab 缓存(如dentry
、inode
等)。
- 分析逻辑:
- 对比不同时间点的
SUnreclaim
值,若持续增加且无合理原因(如系统负载升高),可能存在内核泄漏。 - 结合
slabtop
工具动态监控 Slab 缓存变化。
- 对比不同时间点的
2. 使用 kmemleak 工具检测
- 前提:内核需启用
CONFIG_DEBUG_KMEMLEAK
配置。 - 操作步骤:
- 加载模块:
modprobe kmemleak
。 - 触发扫描:
echo scan > /sys/kernel/debug/kmemleak
。 - 查看结果:
dmesg | grep kmemleak
或cat /sys/kernel/debug/kmemleak
。
- 加载模块:
- 输出解读:
- 泄漏报告包含内存块地址、大小、调用栈,用于定位内核代码中的泄漏点(如未释放的
kmalloc
内存)。
- 泄漏报告包含内存块地址、大小、调用栈,用于定位内核代码中的泄漏点(如未释放的
第五步:计算黑洞内存
1. 黑洞内存定义
- 内核通过直接操作
page
分配的内存(如alloc_pages
),未通过slab
或vmalloc
管理,无法被/proc/meminfo
统计。
2. 计算公式
黑洞内存 = MemTotal - MemFree - Active - Inactive - Slab - KernelStack - PageTables - VmallocUsed
- 参数说明(通过
cat /proc/meminfo
获取):MemTotal
:总内存。MemFree
:空闲内存。Active/Inactive
:活跃/非活跃内存页(对应Active
和Inactive
字段)。Slab
:Slab
字段值(包含SReclaimable
和SUnreclaimable
)。KernelStack
:内核栈内存(可通过cat /proc/kallsyms | grep "\[k\]"$ | wc -l
估算,每个栈约 16KB)。PageTables
:页表内存(PageTables
字段值)。VmallocUsed
:vmalloc
分配的内存(VmallocUsed
字段值)。
3. 异常判断
- 若黑洞内存数值较大(如超过总内存的 10%),可能存在内核模块或驱动通过
alloc_pages
分配了大量未释放的内存。 - 排查方向:
- 检查内核模块(尤其是自定义模块)是否正确释放
page
内存。 - 使用
pagewalk
等工具分析物理页分配情况。
- 检查内核模块(尤其是自定义模块)是否正确释放
第六步:综合分析与优化
1. 优先级排序
问题类型 | 排查优先级 | 常见解决方式 |
---|---|---|
用户进程泄漏 | 高 | 优化程序、修复泄漏、限制内存上限 |
tmpfs 占用过高 | 中 | 清理临时文件、调整挂载大小 |
内核内存泄漏 | 高 | 升级内核、修复驱动/模块泄漏 |
黑洞内存过大 | 低 | 分析内核代码或模块,优化内存分配 |
2. 优化手段
- 用户进程:
- 对异常进程限流(如通过
cgroups
限制内存)。 - 重启或升级存在泄漏的应用。
- 对异常进程限流(如通过
- tmpfs:
- 定期清理
/dev/shm
下的无用文件。 - 将大文件存储改为磁盘而非内存。
- 定期清理
- 内核泄漏:
- 更新内核至稳定版本(尤其是针对已知泄漏的版本)。
- 禁用或调试有问题的内核模块。
- 黑洞内存:
- 若为内核特性(如透明大页、内存压缩),可评估是否需要调整(如
echo never > /sys/kernel/mm/transparent_hugepage/enabled
)。 - 若为自定义模块导致,修复模块中的内存分配逻辑。
- 若为内核特性(如透明大页、内存压缩),可评估是否需要调整(如
总结:排查流程图
通过以上步骤,可逐步定位内存占用高的根源,并针对不同场景采取相应的优化措施。