服务覆盖:昆明·曲靖·玉溪·保山·昭通·丽江·普洱·临沧·楚雄·红河·文山·西双版纳·大理·德宏·怒江·迪庆

最基础的内存查看

eycit 2026-04-18 -5 次阅读 系统安装
---

theme: default themeName: "默认主题" title: "Linux内存越用越少,是谁在偷吃?腾讯架构师教你用这套方法线"


前言

运维工程师最怕半夜被叫起来:服务器内存使用率99%,应用响应慢,SSH都卡了。登上去一看,`free -h`显示内存快没了,top扫一眼发现`something`进程占了大半内存,但当你`kill`掉那个进程后,内存并没有完全释放回来——这时候基本可以判断:你遇到的不是真正的内存泄漏,而是Linux内存管理的正常机制在"背锅"。

但也不排除真有泄漏的情况。问题是:怎么快速区分?怎么准确定位?

先搞清楚:Linux内存都去哪了

Linux的内存分配遵循一个核心原则:分配不等于使用。当你启动一个进程申请100MB内存,它可能实际只占了几MB的物理内存,剩余的虚拟地址空间只是"占着不用"。这叫惰性分配(Lazy Allocation),是操作系统提升内存利用率的标准做法。

所以`free`命令看到的结果需要会解读:

# 最基础的内存查看

free -h

更高信息量的版本,加上 -w(分Wide列显示)

free -wh

实时监控

watch -n 1 free -h

`free`输出的典型解读:

              total        used        free      shared  buff/cache   available

Mem: 31Gi 28Gi 1.2Gi 128Mi 2.1Gi 4.5Gi Swap: 8Gi 128Mi 7.9Gi

重点看`available`而不是`free`:`available`才是真正可供新进程使用的内存(包含可回收的cache),而`free`是彻底空闲的物理内存。Linux会把空闲内存拿来当缓存用,看起来少了但实际上随时可以释放。

场景一:可回收内存——"泄漏"的假象

症状:内存使用率很高,但`buff/cache`占用大量空间,`available`也正常,应用性能没有明显下降。 诊断
# 查看内存分配详情
cat /proc/meminfogrep -E "MemTotalMemFreeMemAvailableBuffersCachedActiveInactiveSwapCached"

重点关注以下指标:

Active(file) + Inactive(file) = 页面缓存(可回收)

SReclaimable = 可回收的slab内存

Shmem = 共享内存(通常计入cache)

slab内存(内核对象缓存)占用

slabtop -ohead -20

或用命令:

cat /proc/slabinfoawk '{print $1, $3}'sort -k2 -rnhead -15
处理方法
# 手动触发内存回收(释放page cache)

sync && echo 3 > /proc/sys/vm/drop_caches

drop_caches的值含义:

1 = 只释放page cache

2 = 释放dentries和inode

3 = 释放page cache + dentries + inode(最彻底)

如果slab占用高,用以下命令排查哪些内核对象吃得多

perf stat -e kmem:* -a sleep 5 # 需要perf工具

⚠️ 生产环境谨慎使用`drop_caches`,可能瞬间增加系统负载。建议用`crond`定时在低峰期执行,并确保先`sync`。

场景二:真正的内存泄漏

症状:内存持续增长,`available`越来越小,`swap`开始被使用,即使内存空闲时也不回收。 诊断步骤
# 第一步:观察趋势,内存是否只增不减

pidstat -r 1 5 # 每秒采样,共5次,看RSS变化

第二步:定位高内存进程

ps aux --sort=-%memhead -20

第三步:确认泄漏速率

选一个可疑进程,记录它的内存使用

ps -o pid,rss,vsz,comm -p

用以下脚本记录增长曲线(每30秒采样)

while true; do ps -o pid,rss -p >> /tmp/mem_leak.log sleep 30 done

第四步:用pmap查看进程的内存映射
# 查看进程内存映射详情
pmap -x sort -k3 -rnhead -20

输出解读:

Address:虚拟地址

Kbytes:虚拟内存大小(RSS是实际物理占用)

RSS:实际使用的物理内存

Mode:内存映射类型([anon]匿名映射 / [stack]栈 / 文件映射)

Mapping:映射的文件或库

如果看到某个anon段持续增长且不释放,基本可以判断泄漏就在那里

第五步:gdb现场调试(生产慎用)
# 用gdb attach到可疑进程,dump堆内存(需要root)

gdb -p (gdb) dump memory /tmp/heap_dump.bin 0x 0x (gdb) quit

用strings查看堆内容(可能找到泄漏线索)

strings /tmp/heap_dump.binhead -100

场景三:OOM Killer在作妖

症状:应用进程被系统莫名杀死,`dmesg`里出现大量`Out of memory`日志,Java/Python等语言进程频繁崩溃。 诊断
# 查看内核日志,看OOM Killer的判决记录
dmesggrep -i "out of memory"
dmesggrep -i "oom"

更友好的格式(需要安装):

oomkill -l # 如果系统有oomkiller-tools

查看每个进程的OOM评分(分数越高越容易被杀)

for f in /proc/*/oom_score; do

p=$(echo $fcut -d/ -f3)
name=$(cat /proc/$p/comm 2>/dev/nullecho "N/A")

score=$(cat $f) echo "PID $p ($name): $score"

donesort -k4 -rnhead -10
控制OOM行为
# 设置进程永不被OOM Killer杀掉(关键进程用)

echo -1000 > /proc//oom_score_adj

有效范围:-1000(永不杀)到 +1000(优先杀)

或者在启动脚本里加:

mysqld_safe --oom_score_adj=-1000

为Java进程关闭OOM Killer自动杀死(不推荐,可能导致系统僵死)

echo 1 > /proc/sys/vm/overcommit_memory

设置内存分配策略为"永远允许超额分配"

预防OOM的措施
# 1. 限制cgroup的内存上限(容器化环境)

编辑 /etc/cgconfig.conf

group memlimit { memory { memory.limit_in_bytes = 4G; memory.soft_limit_in_bytes = 2G; memory.swappiness = 10; } }

2. 调整swappiness(降低换出倾向)

默认60,改成10~30(SSD环境)

sysctl vm.swappiness=15 echo "vm.swappiness=15" >> /etc/sysctl.conf

3. 设置最小空闲内存阈值

sysctl vm.min_free_kbytes=524288 # 512MB预留 echo "vm.min_free_kbytes=524288" >> /etc/sysctl.conf

场景四:swap疯狂使用导致性能塌方

症状:内存并没有满,但swap分区被大量使用,系统开始卡顿。
# 查看各进程的swap使用量

for f in /proc/*/status; do

pid=$(echo $fcut -d/ -f3)
name=$(cat /proc/$pid/comm 2>/dev/nullecho "N/A")
swap=$(grep VmSwap $f 2>/dev/nullawk '{print $2, $3}')

[ -n "$swap" ] && echo "PID $pid ($name): $swap"

donesort -k2 -rnhead -10

swap使用高但物理内存未满的原因通常是:某些进程在早期申请了大量虚拟内存但实际使用不多,Linux会将这些"冷数据"换出到swap。常见于Java应用(默认堆大小偏大)和PostgreSQL(shared_buffers配置)。

优化建议
# 检查进程虚拟内存 vs 物理内存的比率
ps auxawk '{print $6,$11}'sort -rnhead -20

RSS(物理内存)vs VSZ(虚拟内存)差距过大说明有大量未用虚拟空间

调低swappiness,减少swap使用

sysctl vm.swappiness=10

监控swap I/O,确认是否影响性能

iostat -x 1 5

如果 %swpcnt 持续 > 80%,说明swap成为了性能瓶颈

实战脚本:内存健康检查

#!/bin/bash

mem_health_check.sh — Linux内存健康检查脚本

echo "=== Linux 内存健康检查报告 ===" $(date) echo ""

基础信息

echo "【1. 内存概览】"

free -hgrep -E "MemSwap"

echo ""

available 是否健康

avail=$(freeawk '/Mem:/ {print $7}')
total=$(freeawk '/Mem:/ {print $2}')

pct=$(( (total - avail) * 100 / total )) echo "【2. 内存使用率】 ${pct}% (available: ${avail}KB)"

[ $pct -gt 90 ] && echo "⚠️ 内存使用率过高!"echo "✅ 内存使用率正常"

echo ""

page cache占比

cache=$(freeawk '/Mem:/ {print $6}')

echo "【3. Page Cache】 ${cache}KB" [ $cache -gt $((total/2)) ] && echo "📋 page cache较高,可尝试释放:sync && echo 3 > /proc/sys/vm/drop_caches" echo ""

Swap使用率

swaptotal=$(freeawk '/Swap:/ {print $2}')
swapused=$(freeawk '/Swap:/ {print $3}')

if [ $swaptotal -eq 0 ]; then echo "【4. Swap】 未配置" else swappct=$(( swapused * 100 / swaptotal )) echo "【4. Swap使用率】 ${swappct}%" [ $swappct -gt 50 ] && echo "⚠️ Swap使用率过高,建议增加物理内存或优化应用" fi echo ""

top内存进程

echo "【5. Top 5 高内存进程】"

ps aux --sort=-%memhead -6tail -5awk '{printf " PID %s %s: %s%%\n", $2, $11, $4}'

echo ""

OOM记录

oomcount=$(dmesggrep -c "Out of memory" 2>/dev/nullecho "0")

echo "【6. OOM Kill历史】 最近发生 $oomcount 次"

[ $oomcount -gt 0 ] && echo "最近一次OOM详情:" && dmesggrep "Out of memory"tail -1

结语

Linux内存问题的排查,核心在于区分正常机制和真实故障。page cache多不等于有问题,swap被使用也不一定需要加内存。但当你确认是内存泄漏时,`pmap`和`pidstat`是定位问题的两把瑞士军刀。

记住一个原则:监控先于诊断。把内存趋势图画出来,比出事后再去看`free`有用得多。`Prometheus + Grafana`的组合能让你在问题爆发前30分钟就发现苗头,这才是真正的治未病。


希望本文的教程对你有所帮助。如有疑问或需要专业技术支持,可通过以下方式联系我们:

📞 服务热线:13708730161 💬 微信:eyc1689 📧 邮箱:service@eycit.com

易云城IT服务,您身边的IT专家。

上一篇
以管理员身份运行PowerShell...
下一篇
最基础的生成命令...