从内存泄漏诊断到 OOM 根治
📋 目录速览
🎯 内存问题的典型表现
Linux 系统内存问题症状
快速诊断脚本
#!/bin/bash
# memory_quick_check.sh - Linux 内存问题快速诊断
echo "========== Linux 内存快速诊断 =========="
echo "系统: $(uname -s) $(uname -r)"
echo "主机: $(hostname)"
echo "时间: $(date)"
echo ""
# 1. 内存总览
echo "【内存使用情况】"
free -h
echo ""
# 2. 内存使用率
TOTAL=$(free -m | awk 'NR==2{print $2}')
USED=$(free -m | awk 'NR==2{print $3}')
AVAILABLE=$(free -m | awk 'NR==2{print $7}')
USAGE=$(awk "BEGIN {printf \"%.2f\", $USED * 100 / $TOTAL}")
echo "总内存: ${TOTAL}MB"
echo "已使用: ${USED}MB (${USAGE}%)"
echo "可用: ${AVAILABLE}MB"
if [ $AVAILABLE -lt 100 ]; then
echo "⚠️ 警告: 可用内存不足 100MB!"
elif [ $AVAILABLE -lt 500 ]; then
echo "⚠️ 注意: 可用内存偏低"
fi
echo ""
# 3. 内存详细信息
echo "【内存详细信息】"
cat /proc/meminfo | grep -E "MemTotal|MemFree|MemAvailable|Buffers|Cached|SwapTotal|SwapFree|Slab|SReclaimable|SUnreclaim|Dirty|Writeback"
echo ""
# 4. TOP 10 内存占用进程
echo "【TOP 10 内存占用进程】"
ps aux --sort=-%mem | head -11 | awk '{printf "%-8s %-6s %5s %5s %10s %s\n", $1, $2, $3, $4, $6, $11}'
echo ""
# 5. slab 内存使用
echo "【slab 内存 TOP 10】"
if [ -f /proc/slabinfo ]; then
echo "名称 对象数 大小 占用"
cat /proc/slabinfo | tail -n +3 | awk '{
name=$1;
objs=$3;
objsize=$4;
mem=objs*objsize/1024/1024;
if (mem > 1) printf "%-20s %8d %8d %7.2fMB\n", name, objs, objsize, mem
}' | sort -k4 -rn | head -10
else
echo "slabinfo 不可用"
fi
echo ""
# 6. swap 使用情况
echo "【swap 使用情况】"
SWAP_TOTAL=$(free -m | awk 'NR==3{print $2}')
SWAP_USED=$(free -m | awk 'NR==3{print $3}')
if [ $SWAP_TOTAL -gt 0 ]; then
SWAP_PERCENT=$(awk "BEGIN {printf \"%.2f\", $SWAP_USED * 100 / $SWAP_TOTAL}")
echo "Swap 总量: ${SWAP_TOTAL}MB"
echo "Swap 使用: ${SWAP_USED}MB (${SWAP_PERCENT}%)"
if [ $SWAP_USED -gt 0 ]; then
echo "⚠️ 警告: 系统正在使用 swap,可能影响性能"
fi
else
echo "未配置 swap"
fi
echo ""
# 7. OOM 历史
echo "【OOM Killer 历史】"
OOM_COUNT=$(dmesg | grep -c "Out of memory")
if [ $OOM_COUNT -gt 0 ]; then
echo "⚠️ 发现 $OOM_COUNT 次 OOM 事件"
echo "最近 5 次 OOM:"
dmesg | grep "Out of memory" | tail -5
else
echo "✅ 无 OOM 记录"
fi
echo ""
# 8. 内存分配失败
echo "【内存分配失败】"
ALLOC_FAIL=$(dmesg | grep -c "page allocation failure")
if [ $ALLOC_FAIL -gt 0 ]; then
echo "⚠️ 发现 $ALLOC_FAIL 次内存分配失败"
dmesg | grep "page allocation failure" | tail -3
else
echo "✅ 无内存分配失败记录"
fi
echo ""
# 9. 内存压力(如果支持)
if [ -f /proc/pressure/memory ]; then
echo "【内存压力】"
cat /proc/pressure/memory
echo ""
fi
# 10. 系统负载
echo "【系统负载】"
uptime
echo ""
# 11. 诊断结论
echo "【诊断结论】"
ISSUES=0
if [ $AVAILABLE -lt 100 ]; then
echo "❌ 严重: 可用内存不足,需要立即处理"
ISSUES=$((ISSUES + 1))
fi
if [ $SWAP_USED -gt $((SWAP_TOTAL / 2)) ] && [ $SWAP_TOTAL -gt 0 ]; then
echo "❌ 严重: swap 使用率超过 50%"
ISSUES=$((ISSUES + 1))
fi
if [ $OOM_COUNT -gt 0 ]; then
echo "⚠️ 警告: 发现 OOM 事件,系统曾经内存不足"
ISSUES=$((ISSUES + 1))
fi
SUNRECLAIM=$(grep "^SUnreclaim:" /proc/meminfo | awk '{print $2/1024}')
if [ $(echo "$SUNRECLAIM > 500" | bc -l 2>/dev/null || echo 0) -eq 1 ]; then
echo "⚠️ 警告: 不可回收的 slab 内存过多 (${SUNRECLAIM}MB)"
ISSUES=$((ISSUES + 1))
fi
if [ $ISSUES -eq 0 ]; then
echo "✅ 正常: 内存使用正常"
fi
echo ""
echo "========== 诊断完成 ========
🏗️ Linux 内存架构
内存分布概览
内存类型详解
# 查看详细内存信息
$ cat /proc/meminfo
MemTotal: 8388608 kB # 总内存 (8GB)
MemFree: 1048576 kB # 完全空闲的内存
MemAvailable: 3145728 kB # 可用内存(包括可回收的)
Buffers: 204800 kB # 块设备缓冲区
Cached: 2097152 kB # 页缓存
SwapCached: 0 kB # swap 缓存
Active: 4194304 kB # 活跃内存(最近使用)
Inactive: 2097152 kB # 不活跃内存(可回收)
Active(anon): 3145728 kB # 活跃的匿名内存(进程内存)
Inactive(anon): 524288 kB # 不活跃的匿名内存
Active(file): 1048576 kB # 活跃的文件缓存
Inactive(file): 1572864 kB # 不活跃的文件缓存
Unevictable: 0 kB # 不可回收的内存(mlock)
Mlocked: 0 kB # 锁定的内存
SwapTotal: 2097152 kB # swap 总大小 (2GB)
SwapFree: 2097152 kB # swap 空闲
Dirty: 10240 kB # 脏页(待写入磁盘)
Writeback: 0 kB # 正在写回的页
AnonPages: 3670016 kB # 匿名页(进程内存)
Mapped: 524288 kB # 映射的内存(mmap)
Shmem: 262144 kB # 共享内存
Slab: 524288 kB # slab 总大小
SReclaimable: 262144 kB # 可回收的 slab
SUnreclaim: 262144 kB # 不可回收的 slab
KernelStack: 16384 kB # 内核栈
PageTables: 32768 kB # 页表
NFS_Unstable: 0 kB # NFS 不稳定页
Bounce: 0 kB # bounce 缓冲区
WritebackTmp: 0 kB # 临时写回缓冲区
CommitLimit: 6291456 kB # 可分配的总内存
Committed_AS: 5242880 kB # 已分配的内存
VmallocTotal: 34359738367 kB # vmalloc 总大小
VmallocUsed: 102400 kB # vmalloc 已使用
VmallocChunk: 34359635967 kB # vmalloc 最大连续块
HardwareCorrupted: 0 kB # 硬件损坏的内存
AnonHugePages: 524288 kB # 匿名大页
HugePages_Total: 0# 大页总数
HugePages_Free: 0# 大页空闲数
HugePages_Rsvd: 0# 大页保留数
HugePages_Surp: 0# 大页剩余数
Hugepagesize: 2048 kB # 大页大小
DirectMap4k: 524288 kB # 4KB 直接映射
DirectMap2M: 8388608 kB # 2MB 直接映射
内存分配器层次
🐛 内存泄漏的常见原因
通用内存泄漏场景
内存泄漏的识别特征
🔧 内存使用分析工具
基础分析工具
高级分析工具
内存监控脚本
#!/bin/bash
# comprehensive_memory_monitor.sh - 综合内存监控
LOG_DIR="/var/log/memory_monitor"
mkdir -p $LOG_DIR
INTERVAL=60 # 监控间隔(秒)
LOG_FILE="$LOG_DIR/memory_$(date +%Y%m%d).log"
echo "========== 内存监控启动 ==========" | tee -a $LOG_FILE
echo "时间: $(date)" | tee -a $LOG_FILE
echo "主机: $(hostname)" | tee -a $LOG_FILE
echo "内核: $(uname -r)" | tee -a $LOG_FILE
echo "" | tee -a $LOG_FILE
# 记录初始状态
echo "初始内存状态:" | tee -a $LOG_FILE
free -h | tee -a $LOG_FILE
echo "" | tee -a $LOG_FILE
while true; do
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
# 1. 基础内存信息
TOTAL=$(free -m | awk 'NR==2{print $2}')
USED=$(free -m | awk 'NR==2{print $3}')
FREE=$(free -m | awk 'NR==2{print $4}')
AVAILABLE=$(free -m | awk 'NR==2{print $7}')
BUFFERS=$(free -m | awk 'NR==2{print $6}')
# 2. swap 信息
SWAP_TOTAL=$(free -m | awk 'NR==3{print $2}')
SWAP_USED=$(free -m | awk 'NR==3{print $3}')
# 3. slab 信息
SLAB=$(grep "^Slab:" /proc/meminfo | awk '{print $2/1024}')
SRECLAIMABLE=$(grep "^SReclaimable:" /proc/meminfo | awk '{print $2/1024}')
SUNRECLAIM=$(grep "^SUnreclaim:" /proc/meminfo | awk '{print $2/1024}')
# 4. 脏页信息
DIRTY=$(grep "^Dirty:" /proc/meminfo | awk '{print $2/1024}')
WRITEBACK=$(grep "^Writeback:" /proc/meminfo | awk '{print $2/1024}')
# 5. 进程内存 TOP 3
TOP_PROCS=$(ps aux --sort=-%mem | head -4 | tail -3 | awk '{printf "%s:%dMB ", $11, int($6/1024)}')
# 6. 记录日志
printf "%s | Mem: %dM/%dM (Avail:%dM) Swap: %dM/%dM | Slab: %.0fM (SU:%.0fM SR:%.0fM) | Dirty: %.0fM | %s\n" \
"$TIMESTAMP" "$USED" "$TOTAL" "$AVAILABLE" "$SWAP_USED" "$SWAP_TOTAL" \
"$SLAB" "$SUNRECLAIM" "$SRECLAIMABLE" "$DIRTY" "$TOP_PROCS" >> $LOG_FILE
# 7. 检查异常
ALERT=0
# 可用内存过低
if [ $AVAILABLE -lt 500 ]; then
echo "$TIMESTAMP | ⚠️ WARNING: Low memory - Available: ${AVAILABLE}MB" | tee -a $LOG_FILE
ALERT=1
fi
# swap 使用过高
if [ $SWAP_TOTAL -gt 0 ] && [ $SWAP_USED -gt $((SWAP_TOTAL / 2)) ]; then
SWAP_PERCENT=$(awk "BEGIN {printf \"%.1f\", $SWAP_USED * 100 / $SWAP_TOTAL}")
echo "$TIMESTAMP | ⚠️ WARNING: High swap usage - ${SWAP_PERCENT}%" | tee -a $LOG_FILE
ALERT=1
fi
# SUnreclaim 过高
if [ $(echo "$SUNRECLAIM > 1000" | bc -l 2>/dev/null || echo 0) -eq 1 ]; then
echo "$TIMESTAMP | ⚠️ WARNING: High SUnreclaim - ${SUNRECLAIM}MB" | tee -a $LOG_FILE
ALERT=1
fi
# 脏页过多
if [ $(echo "$DIRTY > 500" | bc -l 2>/dev/null || echo 0) -eq 1 ]; then
echo "$TIMESTAMP | ⚠️ WARNING: High dirty pages - ${DIRTY}MB" | tee -a $LOG_FILE
ALERT=1
fi
# 如果有告警,记录详细信息
if [ $ALERT -eq 1 ]; then
DETAIL_FILE="$LOG_DIR/alert_$(date +%Y%m%d_%H%M%S).log"
echo "========== 内存告警详情 ==========" > $DETAIL_FILE
echo "时间: $TIMESTAMP" >> $DETAIL_FILE
echo "" >> $DETAIL_FILE
echo "内存详情:" >> $DETAIL_FILE
free -h >> $DETAIL_FILE
echo "" >> $DETAIL_FILE
echo "TOP 10 进程:" >> $DETAIL_FILE
ps aux --sort=-%mem | head -11 >> $DETAIL_FILE
echo "" >> $DETAIL_FILE
echo "TOP 10 slab:" >> $DETAIL_FILE
sudo cat /proc/slabinfo 2>/dev/null | tail -n +3 | \
awk '{print $1, $3*$4/1024/1024 "MB"}' | sort -k2 -rn | head -10 >> $DETAIL_FILE
echo "" >> $DETAIL_FILE
echo "内存压力:" >> $DETAIL_FILE
cat /proc/pressure/memory 2>/dev/null >> $DETAIL_FILE || echo "不支持" >> $DETAIL_FILE
echo "详细报告: $DETAIL_FILE" | tee -a $LOG_FILE
fi
sleep $INTERVAL
done
内存趋势分析脚本
#!/bin/bash
# memory_trend_analysis.sh - 内存使用趋势分析
LOG_FILE="/var/log/memory_monitor/memory_$(date +%Y%m%d).log"
if [ ! -f "$LOG_FILE" ]; then
echo "日志文件不存在: $LOG_FILE"
exit 1
fi
echo "========== 内存使用趋势分析 =========="
echo "日志文件: $LOG_FILE"
echo "分析时间: $(date)"
echo ""
# 1. 统计信息
TOTAL_LINES=$(grep -c "Mem:" $LOG_FILE)
echo "总记录数: $TOTAL_LINES"
echo ""
# 2. 内存使用趋势
echo "【内存使用趋势】"
grep "Mem:" $LOG_FILE | awk -F'|' '{
split($2, mem, " ");
for (i in mem) {
if (mem[i] ~ /^[0-9]+M\//) {
split(mem[i], parts, "/");
used = parts[1];
gsub(/M/, "", used);
sum += used;
count++;
if (min == 0 || used < min) min = used;
if (used > max) max = used;
}
}
}
END {
if (count > 0) {
avg = sum / count;
printf "平均使用: %.0f MB\n", avg;
printf "最小使用: %d MB\n", min;
printf "最大使用: %d MB\n", max;
printf "波动范围: %d MB\n", max - min;
}
}'
echo ""
# 3. 可用内存趋势
echo "【可用内存趋势】"
grep "Avail:" $LOG_FILE | awk -F'Avail:' '{
split($2, parts, "M");
avail = parts[1];
sum += avail;
count++;
if (min == 0 || avail < min) min = avail;
if (avail > max) max = avail;
}
END {
if (count > 0) {
avg = sum / count;
printf "平均可用: %.0f MB\n", avg;
printf "最小可用: %d MB\n", min;
printf "最大可用: %d MB\n", max;
if (min < 500) {
printf "⚠️ 警告: 可用内存曾低于 500MB\n";
}
}
}'
echo ""
# 4. swap 使用趋势
echo "【swap 使用趋势】"
grep "Swap:" $LOG_FILE | awk -F'Swap:' '{
split($2, parts, "M/");
used = parts[1];
gsub(/ /, "", used);
sum += used;
count++;
if (used > max) max = used;
}
END {
if (count > 0) {
avg = sum / count;
printf "平均使用: %.0f MB\n", avg;
printf "最大使用: %d MB\n", max;
if (max > 100) {
printf "⚠️ 警告: swap 使用超过 100MB\n";
}
}
}'
echo ""
# 5. SUnreclaim 趋势
echo "【SUnreclaim 趋势】"
grep "SU:" $LOG_FILE | awk -F'SU:' '{
split($2, parts, "M");
su = parts[1];
sum += su;
count++;
if (min == 0 || su < min) min = su;
if (su > max) max = su;
}
END {
if (count > 0) {
avg = sum / count;
printf "平均: %.0f MB\n", avg;
printf "最小: %.0f MB\n", min;
printf "最大: %.0f MB\n", max;
printf "增长: %.0f MB\n", max - min;
if (max - min > 100) {
printf "⚠️ 警告: SUnreclaim 增长超过 100MB,可能有内核泄漏\n";
}
}
}'
echo ""
# 6. 内存泄漏检测
echo "【内存泄漏检测】"
FIRST_USED=$(grep "Mem:" $LOG_FILE | head -1 | awk -F'Mem:' '{split($2, a, "M/"); print a[1]}' | tr -d ' ')
LAST_USED=$(grep "Mem:" $LOG_FILE | tail -1 | awk -F'Mem:' '{split($2, a, "M/"); print a[1]}' | tr -d ' ')
if [ -n "$FIRST_USED" ] && [ -n "$LAST_USED" ]; then
DIFF=$((LAST_USED - FIRST_USED))
FIRST_TIME=$(grep "Mem:" $LOG_FILE | head -1 | awk '{print $1, $2}')
LAST_TIME=$(grep "Mem:" $LOG_FILE | tail -1 | awk '{print $1, $2}')
echo "首次记录: $FIRST_TIME - ${FIRST_USED}MB"
echo "最后记录: $LAST_TIME - ${LAST_USED}MB"
echo "内存增长: ${DIFF}MB"
if [ $DIFF -gt 500 ]; then
echo "❌ 严重: 检测到明显的内存增长,可能存在内存泄漏!"
elif [ $DIFF -gt 200 ]; then
echo "⚠️ 警告: 内存有所增长,建议继续观察"
else
echo "✅ 正常: 内存使用稳定"
fi
fi
echo ""
# 7. TOP 进程分析
echo "【TOP 进程统计】"
grep -oP '(?<=\| )[^|]+$' $LOG_FILE | tr ' ' '\n' | grep ':' | \
awk -F':' '{proc[$1] += $2} END {for (p in proc) printf "%-30s %10.0f MB\n", p, proc[p]/NR}' | \
sort -k2 -rn | head -10
echo ""
echo "========== 分析完成 =========="
🔬 内核内存泄漏检测
kmemleak 使用
分析 kmemleak 报告
#!/bin/bash
# analyze_kmemleak.sh - 分析 kmemleak 报告
KMEMLEAK_FILE="/sys/kernel/debug/kmemleak"
if [ ! -f "$KMEMLEAK_FILE" ]; then
echo "❌ kmemleak 不可用"
echo "请检查:"
echo "1. 内核是否支持 CONFIG_DEBUG_KMEMLEAK"
echo "2. debugfs 是否已挂载"
exit 1
fi
echo "========== kmemleak 泄漏分析 =========="
echo "时间: $(date)"
echo ""
# 触发扫描
echo "触发 kmemleak 扫描..."
echo scan > $KMEMLEAK_FILE
sleep 10
# 读取报告
REPORT=$(sudo cat $KMEMLEAK_FILE)
if [ -z "$REPORT" ]; then
echo "✅ 未检测到内存泄漏"
exit 0
fi
# 统计泄漏对象数量
LEAK_COUNT=$(echo "$REPORT" | grep -c "unreferenced object")
echo "检测到 $LEAK_COUNT 个泄漏对象"
echo ""
# 按大小排序
echo "【按大小排序的泄漏 (TOP 10)】"
echo "$REPORT" | grep "unreferenced object" | \
awk '{gsub(/[()]/, "", $6); print $4, $6}' | \
sort -k2 -rn | head -10 | \
awk '{printf "地址: %s 大小: %s 字节\n", $1, $2}'
echo ""
# 统计总泄漏大小
TOTAL_SIZE=$(echo "$REPORT" | grep "unreferenced object" | \
awk '{gsub(/[()]/, "", $6); sum += $6} END {print sum}')
TOTAL_MB=$(echo "scale=2; $TOTAL_SIZE / 1024 / 1024" | bc)
echo "总泄漏大小: ${TOTAL_MB} MB"
echo ""
# 按模块分组
echo "【按模块分组】"
echo "$REPORT" | grep -oP '\[\K[^\]]+' | grep -v "^<" | \
sort | uniq -c | sort -rn | head -10 | \
awk '{printf "%5d 次 %s\n", $1, $2}'
echo ""
# 按进程分组
echo "【按进程分组】"
echo "$REPORT" | grep "comm" | \
awk '{gsub(/"/, "", $2); print $2}' | \
sort | uniq -c | sort -rn | head -10 | \
awk '{printf "%5d 次 %s\n", $1, $2}'
echo ""
# 按年龄分组
echo "【按年龄分组】"
echo "$REPORT" | grep "age" | \
awk '{
age = $NF;
gsub(/[()]/, "", age);
gsub(/s/, "", age);
if (age < 60) bucket = "< 1分钟";
else if (age < 3600) bucket = "< 1小时";
else if (age < 86400) bucket = "< 1天";
else bucket = ">= 1天";
count[bucket]++;
}
END {
for (b in count) printf "%10s: %d 个对象\n", b, count[b]
}'
echo ""
# 保存完整报告
REPORT_FILE="/var/log/kmemleak_$(date +%Y%m%d_%H%M%S).log"
echo "$REPORT" > $REPORT_FILE
echo "完整报告已保存到: $REPORT_FILE"
echo ""
# 建议
echo "【建议】"
if [ $LEAK_COUNT -gt 100 ]; then
echo "❌ 严重: 泄漏对象过多,需要立即处理"
echo " 1. 检查最近加载的内核模块"
echo " 2. 查看 backtrace 定位泄漏代码"
echo " 3. 更新或卸载有问题的驱动"
elif [ $LEAK_COUNT -gt 10 ]; then
echo "⚠️ 警告: 发现一定数量的泄漏"
echo " 1. 继续监控泄漏增长情况"
echo " 2. 分析 backtrace 找出泄漏源"
else
echo "✅ 泄漏数量较少,可能是误报或小问题"
fi
echo ""
echo "========== 分析完成 =========="
使用 ftrace 跟踪内存分配
使用 eBPF/BCC 检测内存泄漏
内核内存泄漏定位流程
👤 用户空间内存泄漏检测
valgrind 使用
# 4. 泄漏类型说明
# definitely lost: 确定泄漏(必须修复)
# - 分配的内存没有任何指针指向
# - 无法被释放
#
# indirectly lost: 间接泄漏
# - 由 definitely lost 导致的泄漏
# - 修复 definitely lost 后会消失
#
# possibly lost: 可能泄漏
# - 指针指向内存块的中间
# - 需要进一步分析
#
# still reachable: 仍可达
# - 程序结束时未释放但仍有指针指向
# - 通常不是问题(如全局变量)
#
# suppressed: 被抑制
# - 使用抑制文件忽略的泄漏
AddressSanitizer (ASan) 使用
# 常用选项:
# detect_leaks=1: 启用泄漏检测
# log_path=file: 输出到文件
# halt_on_error=0: 发现错误后继续运行
# verbosity=1: 详细输出
# 4. 与 valgrind 对比
# ASan 优点:
# - 运行速度快(2-3倍慢)
# - 可以检测更多类型的错误
# - 可以在生产环境使用
#
# ASan 缺点:
# - 需要重新编译
# - 内存开销大(2-3倍)
进程内存分析
用户空间泄漏检测脚本
#!/bin/bash
# user_space_leak_detector.sh - 用户空间内存泄漏检测
PROCESS_NAME=$1
INTERVAL=60
THRESHOLD=100 # MB
DURATION=3600 # 监控时长(秒)
if [ -z "$PROCESS_NAME" ]; then
echo "用法: $0 <进程名>"
echo "示例: $0 mysqld"
exit 1
fi
PID=$(pidof $PROCESS_NAME)
if [ -z "$PID" ]; then
echo "❌ 进程不存在: $PROCESS_NAME"
exit 1
fi
LOG_FILE="/var/log/leak_${PROCESS_NAME}_$(date +%Y%m%d_%H%M%S).log"
echo "========== 用户空间内存泄漏检测 ==========" | tee -a $LOG_FILE
echo "进程: $PROCESS_NAME (PID: $PID)" | tee -a $LOG_FILE
echo "监控间隔: ${INTERVAL}秒" | tee -a $LOG_FILE
echo "泄漏阈值: ${THRESHOLD}MB" | tee -a $LOG_FILE
echo "监控时长: $((DURATION / 3600))小时" | tee -a $LOG_FILE
echo "" | tee -a $LOG_FILE
# 获取初始内存使用
INITIAL_RSS=$(cat /proc/$PID/status | grep VmRSS | awk '{print $2}')
INITIAL_TIME=$(date +%s)
INITIAL_MB=$((INITIAL_RSS / 1024))
echo "初始 RSS: ${INITIAL_MB}MB" | tee -a $LOG_FILE
echo "" | tee -a $LOG_FILE
ELAPSED=0
while [ $ELAPSED -lt $DURATION ]; do
sleep $INTERVAL
ELAPSED=$((ELAPSED + INTERVAL))
# 检查进程是否还在运行
if ! kill -0 $PID 2>/dev/null; then
echo "$(date): ⚠️ 进程已退出" | tee -a $LOG_FILE
exit 0
fi
# 获取当前内存使用
CURRENT_RSS=$(cat /proc/$PID/status | grep VmRSS | awk '{print $2}')
CURRENT_TIME=$(date +%s)
CURRENT_MB=$((CURRENT_RSS / 1024))
# 计算增长
DIFF_RSS=$((CURRENT_RSS - INITIAL_RSS))
DIFF_TIME=$((CURRENT_TIME - INITIAL_TIME))
DIFF_MB=$((DIFF_RSS / 1024))
# 计算增长率(MB/小时)
if [ $DIFF_TIME -gt 0 ]; then
RATE=$(echo "scale=2; $DIFF_MB * 3600 / $DIFF_TIME" | bc)
else
RATE=0
fi
# 记录日志
printf "%s | RSS=%dMB (+%dMB) Rate=%.2fMB/h Elapsed=%dmin\n" \
"$(date '+%Y-%m-%d %H:%M:%S')" "$CURRENT_MB" "$DIFF_MB" "$RATE" "$((ELAPSED / 60))" | \
tee -a $LOG_FILE
# 检查是否超过阈值
if [ $DIFF_MB -gt $THRESHOLD ]; then
echo "$(date): ⚠️ 检测到内存泄漏!" | tee -a $LOG_FILE
echo "内存增长: ${DIFF_MB}MB" | tee -a $LOG_FILE
echo "运行时间: $((DIFF_TIME / 3600))小时" | tee -a $LOG_FILE
echo "增长率: ${RATE}MB/h" | tee -a $LOG_FILE
echo "" | tee -a $LOG_FILE
# 保存详细信息
echo "进程状态:" | tee -a $LOG_FILE
cat /proc/$PID/status | tee -a $LOG_FILE
echo "" | tee -a $LOG_FILE
echo "内存映射 TOP 20:" | tee -a $LOG_FILE
cat /proc/$PID/smaps | grep -E "^[0-9a-f]|^Size:|^Rss:" | \
paste - - - | sort -k6 -rn | head -20 | tee -a $LOG_FILE
echo "" | tee -a $LOG_FILE
# 如果增长严重,可以选择生成 core dump
if [ $DIFF_MB -gt $((THRESHOLD * 2)) ]; then
echo "$(date): ❌ 严重泄漏,生成 core dump" | tee -a $LOG_FILE
gcore -o /tmp/core_${PROCESS_NAME}_$(date +%Y%m%d_%H%M%S) $PID
fi
fi
done
echo "" | tee -a $LOG_FILE
echo "========== 监控完成 ==========" | tee -a $LOG_FILE
echo "最终 RSS: ${CURRENT_MB}MB" | tee -a $LOG_FILE
echo "总增长: ${DIFF_MB}MB" | tee -a $LOG_FILE
echo "平均增长率: ${RATE}MB/h" | tee -a $LOG_FILE
echo "日志文件: $LOG_FILE" | tee -a $LOG_FILE
八、特殊场景内存问题
8.1 网络子系统内存泄漏
conntrack 连接跟踪泄漏
在防火墙、NAT 网关等场景中,conntrack 表满是常见问题:
解决方案:
SKB (Socket Buffer) 泄漏
网络数据包处理中的 SKB 泄漏:
检测脚本:
#!/bin/bash
# skb_leak_detector.sh
echo "========== SKB 泄漏检测 =========="
# 获取初始值
INITIAL_ALLOC=$(cat /proc/net/sockstat | grep TCP | awk '{print $10}')
INITIAL_MEM=$(cat /proc/net/sockstat | grep TCP | awk '{print $12}')
echo "初始 TCP alloc: $INITIAL_ALLOC"
echo "初始 TCP mem: $INITIAL_MEM pages"
echo ""
for i in {1..10}; do
sleep 60
CURRENT_ALLOC=$(cat /proc/net/sockstat | grep TCP | awk '{print $10}')
CURRENT_MEM=$(cat /proc/net/sockstat | grep TCP | awk '{print $12}')
DIFF_ALLOC=$((CURRENT_ALLOC - INITIAL_ALLOC))
DIFF_MEM=$((CURRENT_MEM - INITIAL_MEM))
echo "$(date): alloc=$CURRENT_ALLOC (+$DIFF_ALLOC) mem=$CURRENT_MEM (+$DIFF_MEM pages)"
if [ $DIFF_MEM -gt 1000 ]; then
echo "⚠️ 检测到 SKB 内存持续增长!"
echo "检查网络驱动和协议栈是否正常释放 SKB"
fi
done
8.2 文件系统缓存问题
Page Cache 不释放
问题场景:
C 代码示例:
8.3 容器环境内存问题
Cgroup 内存限制与泄漏
容器内存泄漏检测:
#!/bin/bash
# container_memory_monitor.sh
CONTAINER_ID=$1
THRESHOLD_PERCENT=80
if [ -z "$CONTAINER_ID" ]; then
echo "用法: $0 <容器ID>"
exit 1
fi
echo "监控容器: $CONTAINER_ID"
while true; do
# 获取内存使用情况
STATS=$(docker stats --no-stream --format "{{.MemUsage}}" $CONTAINER_ID)
USED=$(echo $STATS | awk '{print $1}' | sed 's/GiB//;s/MiB//')
LIMIT=$(echo $STATS | awk '{print $3}' | sed 's/GiB//;s/MiB//')
# 计算使用百分比
PERCENT=$(docker stats --no-stream --format "{{.MemPerc}}" $CONTAINER_ID | sed 's/%//')
echo "$(date): 内存使用 $USED / $LIMIT ($PERCENT%)"
if (( $(echo "$PERCENT > $THRESHOLD_PERCENT" | bc -l) )); then
echo "⚠️ 内存使用超过阈值!"
# 查看容器内进程内存使用
echo "容器内 TOP 进程:"
docker exec $CONTAINER_ID ps aux --sort=-%mem | head -10
# 检查是否有 OOM 事件
CGROUP_PATH=$(docker inspect -f '{{.Id}}' $CONTAINER_ID)
OOM_COUNT=$(cat /sys/fs/cgroup/memory/docker/$CGROUP_PATH/memory.oom_control 2>/dev/null | grep oom_kill | awk '{print $2}')
echo "OOM 次数: $OOM_COUNT"
fi
sleep 60
done
容器内存泄漏排查
容器资源限制最佳实践:
# Docker Compose 示例
version: '3'
services:
webapp:
image: myapp:latest
deploy:
resources:
limits:
memory: 2G # 硬限制
cpus: '2'
reservations:
memory: 1G # 软限制
# 启用 OOM Killer
oom_kill_disable: false
# 设置 OOM 分数调整
oom_score_adj: 500
Kubernetes 示例:
apiVersion: v1
kind: Pod
metadata:
name: webapp
spec:
containers:
- name: app
image: myapp:latest
resources:
requests:
memory: "1Gi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "2"
# 存活探针
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
九、Slab 内存分析
9.1 Slab 内存基础
Slab 是内核对象缓存机制,用于高效分配常用内核对象:
9.2 Slab 泄漏检测
检测脚本:
#!/bin/bash
# slab_leak_detector.sh
LOG_FILE="/var/log/slab_leak_$(date +%Y%m%d_%H%M%S).log"
INTERVAL=300 # 5分钟
THRESHOLD=100 # MB
echo "========== Slab 内存泄漏检测 ==========" | tee -a $LOG_FILE
echo "监控间隔: ${INTERVAL}秒" | tee -a $LOG_FILE
echo "泄漏阈值: ${THRESHOLD}MB" | tee -a $LOG_FILE
echo "" | tee -a $LOG_FILE
# 创建临时文件存储初始状态
TEMP_INITIAL="/tmp/slab_initial_$$"
TEMP_CURRENT="/tmp/slab_current_$$"
# 获取初始 slab 使用情况
cat /proc/slabinfo | tail -n +3 | awk '{print $1, $2, $3, $4}' > $TEMP_INITIAL
echo "初始 Slab 使用 TOP 10:" | tee -a $LOG_FILE
cat /proc/slabinfo | tail -n +3 | \
awk '{printf "%-30s %10d objs %8.2f MB\n", $1, $2, $2*$4/1024/1024}' | \
sort -k4 -rn | head -10 | tee -a $LOG_FILE
echo "" | tee -a $LOG_FILE
while true; do
sleep $INTERVAL
# 获取当前 slab 使用情况
cat /proc/slabinfo | tail -n +3 | awk '{print $1, $2, $3, $4}' > $TEMP_CURRENT
echo "$(date '+%Y-%m-%d %H:%M:%S')" | tee -a $LOG_FILE
echo "----------------------------------------" | tee -a $LOG_FILE
# 比较变化
join $TEMP_INITIAL $TEMP_CURRENT | \
awk '{
name=$1;
initial_objs=$2;
initial_total=$3;
objsize=$4;
current_objs=$5;
current_total=$6;
diff_objs = current_objs - initial_objs;
diff_mb = diff_objs * objsize / 1024 / 1024;
if (diff_mb > 10) { # 增长超过 10MB
printf "%-30s: %+10d objs %+8.2f MB (%.2f%%)\n",
name, diff_objs, diff_mb,
(current_objs - initial_objs) * 100.0 / initial_objs;
}
}' | sort -k4 -rn | tee -a $LOG_FILE
# 检查总体 slab 增长
INITIAL_SLAB=$(cat /proc/meminfo | grep "^Slab:" | awk '{print $2}')
CURRENT_SLAB=$(cat /proc/meminfo | grep "^Slab:" | awk '{print $2}')
DIFF_SLAB=$(( (CURRENT_SLAB - INITIAL_SLAB) / 1024 ))
echo "" | tee -a $LOG_FILE
echo "总 Slab 内存: $((CURRENT_SLAB / 1024))MB (增长: ${DIFF_SLAB}MB)" | tee -a $LOG_FILE
if [ $DIFF_SLAB -gt $THRESHOLD ]; then
echo "⚠️ 检测到 Slab 内存泄漏!" | tee -a $LOG_FILE
# 显示增长最多的 slab
echo "" | tee -a $LOG_FILE
echo "增长最多的 Slab 缓存:" | tee -a $LOG_FILE
join $TEMP_INITIAL $TEMP_CURRENT | \
awk '{
name=$1; initial=$2; objsize=$4; current=$5;
diff = (current - initial) * objsize / 1024 / 1024;
if (diff > 0) print name, diff "MB";
}' | sort -k2 -rn | head -5 | tee -a $LOG_FILE
fi
echo "" | tee -a $LOG_FILE
done
# 清理
rm -f $TEMP_INITIAL $TEMP_CURRENT
9.3 常见 Slab 泄漏场景
dentry/inode 缓存泄漏
kmalloc 泄漏
buffer_head 泄漏
十、OOM Killer 机制详解
10.1 OOM Killer 工作原理
当系统内存不足时,OOM Killer 会选择并杀死进程以释放内存:
内存分配请求
↓
检查可用内存
↓
内存不足?
↓ 是
尝试回收内存(page cache, swap等)
↓
仍然不足?
↓ 是
触发 OOM Killer
↓
计算每个进程的 oom_score
↓
选择 oom_score 最高的进程
↓
发送 SIGKILL 信号杀死进程
↓
释放内存
10.2 OOM Score 计算
每个进程都有一个 oom_score,分数越高越容易被杀死:
OOM Score 计算因素:
10.3 OOM 日志分析
OOM 发生时会在系统日志中留下详细信息:
典型 OOM 日志解析:
关键信息解读:
10.4 OOM 保护策略
调整 oom_score_adj
配置 OOM Killer 行为
应用层保护
10.5 OOM 预防措施
#!/bin/bash
# oom_prevention.sh - OOM 预防监控
THRESHOLD=90 # 内存使用阈值(百分比)
CHECK_INTERVAL=60
while true; do
# 获取内存使用百分比
MEM_PERCENT=$(free | grep Mem | awk '{printf "%.0f", $3/$2 * 100}')
echo "$(date): 内存使用 ${MEM_PERCENT}%"
if [ $MEM_PERCENT -gt $THRESHOLD ]; then
echo "⚠️ 内存使用超过阈值!"
# 1. 记录当前状态
echo "=== 内存状态 ===" >> /var/log/oom_prevention.log
free -h >> /var/log/oom_prevention.log
# 2. 记录 TOP 进程
echo "=== TOP 内存进程 ===" >> /var/log/oom_prevention.log
ps aux --sort=-%mem | head -20 >> /var/log/oom_prevention.log
# 3. 尝试释放缓存
sync
echo 1 > /proc/sys/vm/drop_caches
# 4. 发送告警
echo "内存使用 ${MEM_PERCENT}% 超过阈值 ${THRESHOLD}%" | \
mail -s "OOM 预警" admin@example.com
# 5. 可选:重启占用内存最多的非关键服务
# systemctl restart some_service
fi
sleep $CHECK_INTERVAL
done
十一、内存碎片问题
11.1 内存碎片类型
内存碎片分为外部碎片和内部碎片:
11.2 内存碎片整理
11.3 应用层避免碎片
十二、实战案例分析
案例 1:内核模块内存泄漏
问题现象
某路由器设备运行数天后,slab 内存持续增长:
排查过程
根本原因
分析内核模块代码发现问题:
解决方案
临时缓解措施:
效果
案例 2:长期运行系统的缓慢内存增长
问题现象
某数据采集服务器运行 3 个月后内存使用持续增长:
排查过程
根本原因
代码分析发现文件句柄泄漏:
解决方案
系统层面优化:
效果
十三、预防与监控方案
13.1 开发阶段预防
代码审查关注点
内存管理检查项:
□ 所有 malloc/new 都有对应的 free/delete
□ 使用智能指针(C++)或 RAII 模式
□ 循环中的内存分配是否会累积
□ 是否有内存泄漏检测工具集成(Valgrind, ASan)
□ 缓存是否有大小限制和过期策略
□ 文件句柄、socket 等资源是否正确关闭
□ 异常处理路径是否释放资源
□ 是否有内存使用监控和告警
编译时检查
# ✅ 使用 AddressSanitizer 编译
$ gcc-fsanitize=address -g-o app app.c
# ✅ 使用 LeakSanitizer
$ gcc-fsanitize=leak -g-o app app.c
# ✅ 使用静态分析工具
$ cppcheck--enable=all --inconclusive src/
# ✅ 使用 Clang Static Analyzer
$ scan-buildmake
自动化监控脚本
#!/bin/bash
# comprehensive_memory_monitor.sh - 综合内存监控
INTERVAL=300 # 5分钟
LOG_DIR="/var/log/memory_monitor"
ALERT_EMAIL="admin@example.com"
mkdir -p $LOG_DIR
# 阈值配置
MEM_THRESHOLD=85 # 内存使用率阈值(%)
SLAB_THRESHOLD=30 # Slab 占比阈值(%)
PROCESS_GROWTH_MB=100 # 进程内存增长阈值(MB/小时)
# 初始化进程内存记录
declare -A PROCESS_MEM_INITIAL
declare -A PROCESS_MEM_TIME
log_message() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a $LOG_DIR/monitor.log
}
check_system_memory() {
local mem_percent=$(free | grep Mem | awk '{printf "%.0f", $3/$2 * 100}')
local mem_available=$(free -h | grep Mem | awk '{print $7}')
log_message "系统内存使用: ${mem_percent}% (可用: ${mem_available})"
if [ $mem_percent -gt $MEM_THRESHOLD ]; then
log_message "⚠️ 内存使用超过阈值 ${MEM_THRESHOLD}%"
# 记录详细信息
free -h > $LOG_DIR/memory_$(date +%Y%m%d_%H%M%S).txt
ps aux --sort=-%mem | head -20 >> $LOG_DIR/memory_$(date +%Y%m%d_%H%M%S).txt
# 发送告警
echo "内存使用 ${mem_percent}% 超过阈值" | \
mail -s "内存告警 - $(hostname)" $ALERT_EMAIL
return 1
fi
return 0
}
check_slab_memory() {
local slab_kb=$(cat /proc/meminfo | grep "^Slab:" | awk '{print $2}')
local total_kb=$(cat /proc/meminfo | grep "^MemTotal:" | awk '{print $2}')
local slab_percent=$(echo "scale=2; $slab_kb * 100 / $total_kb" | bc)
log_message "Slab 内存: $((slab_kb / 1024))MB (${slab_percent}%)"
if (( $(echo "$slab_percent > $SLAB_THRESHOLD" | bc -l) )); then
log_message "⚠️ Slab 内存占比过高"
# 记录 TOP slab 使用者
cat /proc/slabinfo | tail -n +3 | \
awk '{printf "%-30s %10d objs %8.2f MB\n", $1, $2, $2*$4/1024/1024}' | \
sort -k4 -rn | head -20 > $LOG_DIR/slabinfo_$(date +%Y%m%d_%H%M%S).txt
return 1
fi
return 0
}
check_process_growth() {
local current_time=$(date +%s)
# 检查所有进程
while read pid comm rss; do
local key="${pid}_${comm}"
# 如果是第一次记录
if [ -z "${PROCESS_MEM_INITIAL[$key]}" ]; then
PROCESS_MEM_INITIAL[$key]=$rss
PROCESS_MEM_TIME[$key]=$current_time
continue
fi
# 计算增长
local initial_rss=${PROCESS_MEM_INITIAL[$key]}
local initial_time=${PROCESS_MEM_TIME[$key]}
local diff_rss=$((rss - initial_rss))
local diff_time=$((current_time - initial_time))
# 计算增长率(MB/小时)
if [ $diff_time -gt 0 ]; then
local growth_rate=$(echo "scale=2; $diff_rss * 3600 / $diff_time / 1024" | bc)
if (( $(echo "$growth_rate > $PROCESS_GROWTH_MB" | bc -l) )); then
log_message "⚠️ 进程 $comm (PID: $pid) 内存增长 ${growth_rate}MB/h"
# 记录进程详情
echo "=== 进程 $pid ($comm) ===" >> $LOG_DIR/process_growth.log
cat /proc/$pid/status >> $LOG_DIR/process_growth.log 2>/dev/null
echo "" >> $LOG_DIR/process_growth.log
fi
fi
done < <(ps -eo pid,comm,rss --no-headers | awk '$3 > 102400') # 只检查 >100MB 的进程
}
check_oom_events() {
local oom_count=$(dmesg | grep -c "Out of memory")
if [ $oom_count -gt 0 ]; then
log_message "⚠️ 检测到 $oom_count 次 OOM 事件"
dmesg | grep -A 20 "Out of memory" | tail -50 > $LOG_DIR/oom_$(date +%Y%m%d_%H%M%S).log
fi
}
# 主循环
log_message "========== 内存监控启动 =========="
while true; do
check_system_memory
check_slab_memory
check_process_growth
check_oom_events
sleep $INTERVAL
done
13.2 日志分析
#!/bin/bash
# analyze_memory_logs.sh - 分析内存日志
LOG_FILE="/var/log/memory_monitor/monitor.log"
echo "========== 内存日志分析 =========="
echo ""
# 1. 统计告警次数
echo "=== 告警统计 ==="
echo "内存告警次数: $(grep -c "内存使用超过阈值" $LOG_FILE)"
echo "Slab 告警次数: $(grep -c "Slab 内存占比过高" $LOG_FILE)"
echo "进程增长告警: $(grep -c "进程.*内存增长" $LOG_FILE)"
echo "OOM 事件: $(grep -c "检测到.*OOM" $LOG_FILE)"
echo ""
# 2. 找出内存增长最快的进程
echo "=== 内存增长最快的进程 ==="
grep "进程.*内存增长" $LOG_FILE | \
awk '{print $(NF-1), $(NF-3)}' | \
sort -k1 -rn | head -10
echo ""
# 3. 分析内存使用趋势
echo "=== 内存使用趋势(最近 24 小时)==="
grep "系统内存使用" $LOG_FILE | tail -288 | \ # 24h * 60min / 5min
awk '{print $1, $2, $4}' | \
awk -F'%' '{print $1}' | \
awk '{sum+=$NF; count++} END {print "平均:", sum/count "%"}'
echo ""
# 4. 生成报告
REPORT_FILE="/var/log/memory_monitor/report_$(date +%Y%m%d).txt"
{
echo "内存监控报告 - $(date)"
echo "======================================"
echo ""
echo "告警统计:"
echo " 内存告警: $(grep -c "内存使用超过阈值" $LOG_FILE)"
echo " Slab 告警: $(grep -c "Slab 内存占比过高" $LOG_FILE)"
echo " OOM 事件: $(grep -c "检测到.*OOM" $LOG_FILE)"
echo ""
echo "问题进程:"
grep "进程.*内存增长" $LOG_FILE | tail -20
} > $REPORT_FILE
echo "报告已生成: $REPORT_FILE"
十四、常见问题解答(FAQ)
Q1: 如何区分内存泄漏和正常的内存增长?
正常内存增长特征:
增长后趋于稳定
有明确的业务原因(如缓存、连接池)
可以通过释放缓存回收
内存泄漏特征:
持续线性增长,不会稳定
无法通过正常手段回收
最终导致 OOM
判断方法:
Q2: buff/cache 占用很高是否正常?
完全正常。Linux 会使用空闲内存作为文件缓存(page cache)来提升性能。
只有当 available 很低时才需要担心。
Q3: 如何快速定位是哪个进程导致的内存泄漏?
Q4: 内核模块泄漏如何排查?
# 1. 启用 kmemleak
$ mount -t debugfs nodev /sys/kernel/debug
$ echo scan > /sys/kernel/debug/kmemleak
$ cat /sys/kernel/debug/kmemleak
# 2. 检查 slab 使用
$ cat /proc/slabinfo | sort -k3 -rn | head -20
# 3. 使用 ftrace 追踪内核内存分配
$ cd /sys/kernel/debug/tracing
$ echo function > current_tracer
$ echo kmalloc > set_ftrace_filter
$ echo 1 > tracing_on
$ cat trace
Q5: 容器被 OOM Kill 但看起来内存还有剩余?
这是因为容器有 cgroup 内存限制:
Q6: 为什么执行 drop_caches 后内存还是很高?
drop_caches 只能释放 page cache、dentries 和 inodes,不能释放:
Q7: 如何设置合理的 oom_score_adj?
Q8: 内存碎片化严重如何处理?
# 1. 检查碎片化程度
$ cat /proc/buddyinfo
$ cat /sys/kernel/debug/extfrag/extfrag_index
# 2. 手动整理内存
$ echo 1 > /proc/sys/vm/compact_memory
# 3. 配置自动整理
$ sysctl -w vm.compaction_proactiveness=20
# 4. 使用大页内存
$ echo 1024 > /proc/sys/vm/nr_hugepages
# 5. 应用层使用内存池避免碎片
Q9: 如何在生产环境安全地排查内存问题?
十五、总结
核心要点
内存问题排查的关键在于:
快速定位问题类型
用户空间泄漏 → 使用 Valgrind、ASan、进程监控
内核空间泄漏 → 使用 kmemleak、slab 分析
缓存问题 → 检查 page cache、slab
OOM 问题 → 分析日志、调整策略
系统化的排查流程
预防胜于治疗
代码审查和静态分析
编译时检查(ASan、LSan)
单元测试覆盖内存场景
生产环境持续监控
工具选择
开发阶段:Valgrind、ASan、静态分析
测试阶段:压力测试、长时间运行测试
生产环境:轻量级监控、eBPF、日志分析
最佳实践
// ✅ 内存管理黄金法则
1.谁分配谁释放
2.使用RAII或智能指针
3.避免裸指针传递所有权
4.异常安全的资源管理
5.缓存必须有界和过期
6.定期审查和测试
工具速查表
| 场景 | 推荐工具 | 命令示例 |
|---|
| 快速检查系统内存 | free, top | free -h |
| 查看进程内存 | ps, smem | ps aux --sort=-%mem |
| 用户空间泄漏检测 | Valgrind | valgrind --leak-check=full ./app |
| 内核空间泄漏检测 | kmemleak | cat /sys/kernel/debug/kmemleak |
| Slab 分析 | slabinfo | cat /proc/slabinfo |
| OOM 日志分析 | dmesg | dmesg \| grep -i oom |
| 容器内存监控 | docker stats | docker stats --no-stream |
| Java 堆分析 | jmap, MAT | jmap -dump:file=heap.bin <pid> |
| 实时追踪 | eBPF/BCC | memleak -p <pid> |
参考资源
Linux 内核文档:Documentation/vm/
Valgrind 用户手册:https://valgrind.org/docs/manual/
eBPF/BCC 工具集:https://github.com/iovisor/bcc
Java 内存分析:Eclipse MAT
容器监控:Prometheus + Grafana
内存问题的解决不是一蹴而就的,需要持续的监控、分析和优化。保持对系统内存使用的敏感度,在问题变严重之前及时发现和处理,是保障系统稳定运行的关键。