system-memory-inspector

GitHub 作者 SRE-Team

Linux 系统级内存泄漏巡检:定时扫描所有进程内存,记录系统内存全景, 通过增长趋势分析识别异常进程,输出排查思路和可疑进程列表。 当用户提到"系统内存巡检"、"全进程内存扫描"、"内存泄漏排查"、"环境内存分析"、 "定时统计所有进程"、"系统内存记录"、"找出泄漏进程"时触发。

安装 / 下载方式

TotalClaw CLI推荐
totalclaw install github:LeoYeAI~openclaw-master-skills~system-memory-inspector
cURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/github%3ALeoYeAI~openclaw-master-skills~system-memory-inspector/file -o system-memory-inspector.md
# 系统内存泄漏巡检技能

## 核心逻辑

```
定时扫描所有进程 RSS → 持久化存储 → 跨时间对比 → 识别增长异常进程 → 输出排查清单
```

## 巡检流程

### 1. 数据采集
扫描 `/proc/<pid>/status` 获取所有进程:
- PID、进程名、RSS、VmSize、线程数、运行时间

### 2. 数据存储
按时间戳存储快照:
```
/var/log/memory-inspector/
├── 20240115_090000.snapshot      # 完整快照
├── 20240115_090500.snapshot
└── trending/
    ├── java-app.dat              # 单个进程历史趋势
    ├── python-worker.dat
    └── ...
```

### 3. 分析算法

#### 进程级增长检测
```
对每个进程:
  读取最近N次RSS记录 → 计算增长率 → 标记异常等级
```

#### 系统级健康评分
```
系统评分 = 100 - Σ(异常进程权重)
  确认泄漏进程: -20分/个
  疑似泄漏进程: -10分/个
  高内存进程: -5分/个 (>1GB且持续增长)
```

### 4. 输出报告

**包含内容**:
1. **系统内存概况**: 总内存、已用、缓存、可用
2. **TOP 内存进程**: 当前占用最高的进程
3. **增长异常进程**: 疑似/确认泄漏的进程清单
4. **排查思路**: 针对异常进程的排查建议

---

## 使用方式

```bash
# 手动执行一次巡检
./system-memory-scan.sh

# 定时巡检 (crontab 每5分钟)
*/5 * * * * /path/to/system-memory-scan.sh >> /var/log/memory-inspector/cron.log 2>&1

# 查看最新报告
cat /var/log/memory-inspector/latest-report.txt
```

---

## 输出示例

```
========== 系统内存巡检报告 ==========
巡检时间: 2024-01-15 09:15:00
系统评分: 75/100 (良好)

【系统概况】
总内存: 32GB | 已用: 24GB(75%) | 可用: 6GB | 缓存: 2GB

【TOP 10 内存进程】
PID    进程名              RSS(MB)   运行时间    状态
1234   java-app            8192      3d12h       正常
5678   python-worker       4096      5d08h       疑似泄漏 ⚠️
9012   nginx               512       10d00h      正常
...

【增长异常进程】

⚠️  疑似泄漏 (增长率 30-100MB/h):
  PID 5678 python-worker
    当前RSS: 4096MB (5分钟前: 3950MB)
    增长率: 55MB/h
    趋势: 近1小时持续增长
    建议: 关注业务负载,准备生成堆内存分析

🚨 确认泄漏 (增长率 >100MB/h):
  PID 3456 node-server
    当前RSS: 2048MB (5分钟前: 1800MB)
    增长率: 150MB/h  
    趋势: 近30分钟加速增长
    建议: 立即排查,考虑重启止损

【排查思路】

1. 对于 python-worker (PID 5678):
   - 检查是否有未关闭的数据库连接
   - 查看日志是否有任务积压
   - 使用 `tracemalloc` 生成内存快照对比

2. 对于 node-server (PID 3456):
   - 检查是否有未释放的 Buffer/Stream
   - 查看事件监听器是否累积
   - 使用 Chrome DevTools 生成 heap snapshot

3. 系统级建议:
   - 当前系统内存使用率75%,建议清理缓存或扩容
   - 2个进程存在泄漏风险,建议1小时内处理
```

---

## 核心算法

### 进程增长率计算
```bash
# 读取进程历史 (最近6个采样点)
history=$(tail -6 /var/log/memory-inspector/trending/${pid}.dat)
# 计算线性斜率 (简化: 首尾差分 / 时间跨度)
growth_rate=$(( (current - old) * 3600 / time_span_seconds ))  # MB/h
```

### 异常等级判定
| 增长率 | 等级 | 动作 |
|--------|------|------|
| < 10 MB/h | 正常 | 记录即可 |
| 10-50 MB/h | 关注 | 标记,增加采样频率 |
| 50-100 MB/h | 疑似泄漏 | 输出告警,建议排查 |
| > 100 MB/h | 确认泄漏 | 紧急告警,建议立即处理 |

### 降噪处理
- 过滤短时进程 (< 5分钟,可能是临时命令)
- 过滤系统进程 (kernel、kthreadd 等)
- 过滤已知大内存应用 (如 redis 缓存服务,看配置而非增长)
```
---

## scripts/system-memory-scan.sh

```bash
#!/bin/bash
#
# 系统级内存巡检脚本
# 功能: 扫描所有进程,分析内存增长趋势,生成排查报告
#

set -e

# 配置
INSPECTOR_DIR="/var/log/memory-inspector"
SNAPSHOT_DIR="$INSPECTOR_DIR"
TREND_DIR="$INSPECTOR_DIR/trending"
REPORT_FILE="$INSPECTOR_DIR/latest-report.txt"
MAX_HISTORY=10                    # 保留最近10个快照
SAMPLE_INTERVAL=300               # 默认采样间隔5分钟(用于计算增长率)

# 阈值 (MB/h)
THRESHOLD_NORMAL=10
THRESHOLD_ATTENTION=50
THRESHOLD_SUSPECT=100
THRESHOLD_CONFIRM=100

# 初始化目录
mkdir -p "$SNAPSHOT_DIR" "$TREND_DIR"

# 当前时间
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
DATE_STR=$(date '+%Y%m%d_%H%M%S')

# ==================== 1. 采集所有进程数据 ====================

collect_snapshot() {
    local snapshot_file="$SNAPSHOT_DIR/${DATE_STR}.snapshot"
    
    # 采集: PID, 进程名, RSS(MB), VmSize(MB), 运行时间(秒), 命令行
    echo "# 系统内存快照 $TIMESTAMP" > "$snapshot_file"
    echo "# PID|NAME|RSS|VSZ|TIME|CMD" >> "$snapshot_file"
    
    for pid_dir in /proc/[0-9]*; do
        pid=$(basename "$pid_dir")
        
        # 跳过已消失进程
        [ -f "$pid_dir/status" ] || continue
        
        # 读取数据
        name=$(grep ^Name: "$pid_dir/status" 2>/dev/null | awk '{print $2}' || echo "unknown")
        rss=$(grep ^VmRSS: "$pid_dir/status" 2>/dev/null | awk '{print $2}' || echo "0")  # KB
        vsz=$(grep ^VmSize: "$pid_dir/status" 2>/dev/null | awk '{print $2}' || echo "0") # KB
        
        # 转换为MB
        rss_mb=$((rss / 1024))
        vsz_mb=$((vsz / 1024))
        
        # 运行时间 (从/proc/uptime - /proc/[pid]/stat 计算,简化用ps)
        # 这里用etime的秒数近似
        etime=$(ps -p "$pid" -o etimes= 2>/dev/null | tr -d ' ' || echo "0")
        
        # 命令行 (截断)
        cmd=$(cat "$pid_dir/cmdline" 2>/dev/null | tr '\0' ' ' | cut -c1-50 || echo "[kernel]")
        [ -z "$cmd" ] && cmd="[$name]"
        
        # 过滤系统进程和短时进程
        [ "$etime" -lt 300 ] && continue  # 跳过运行<5分钟的进程
        [[ "$name" == "kthreadd" || "$name" == "migration" || "$name" == "watchdog" ]] && continue
        
        echo "$pid|$name|$rss_mb|$vsz_mb|$etime|$cmd" >> "$snapshot_file"
    done
    
    echo "$snapshot_file"
}

# ==================== 2. 更新进程趋势数据 ====================

update_trends() {
    local snapshot_file=$1
    
    # 读取当前快照中的所有进程
    tail -n +3 "$snapshot_file" | while IFS='|' read -r pid name rss vsz etime cmd; do
        trend_file="$TREND_DIR/${pid}.dat"
        
        # 格式: 时间戳 RSS
        echo "$(date +%s) $rss" >> "$trend_file"
        
        # 只保留最近20个样本 (约100分钟历史)
        tail -n 20 "$trend_file" > "${trend_file}.tmp" && mv "${trend_file}.tmp" "$trend_file"
    done
}

# ==================== 3. 分析增长率 ====================

calculate_growth_rate() {
    local pid=$1
    local trend_file="$TREND_DIR/${pid}.dat"
    
    [ -f "$trend_file" ] || return
    
    local line_count=$(wc -l < "$trend_file")
    [ "$line_count" -lt 3 ] && return  # 需要至少3个点
    
    # 读取首尾计算斜率 (简化算法)
    local first=$(head -1 "$trend_file")
    local last=$(tail -1 "$trend_file")
    
    local first_time=$(echo "$first" | awk '{print $1}')
    local first_rss=$(echo "$first" | awk '{print $2}')
    local last_time=$(echo "$last" | awk '{print $1}')
    local last_rss=$(echo "$last" | awk '{print $2}')
    
    local time_diff=$((last_time - first_time))
    [ "$time_diff" -lt 60 ] && return  # 时间跨度太小
    
    local rss_diff=$((last_rss - first_rss))
    
    # 计算 MB/h
    local rate=$(( rss_diff * 3600 / time_diff ))
    
    # 只返回正增长
    [ "$rate" -gt 0 ] && echo "$rate" || echo "0"
}

# ==================== 4. 生成报告 ====================

generate_report() {
    local snapshot_file=$1
    local report="$REPORT_FILE"
    
    # 系统内存信息
    local mem_total=$(grep MemTotal /proc/meminfo | awk '{print $2}')
    local mem_available=$(grep MemAvailable /proc/meminfo | awk '{print $2}')
    local mem_used=$((mem_total - mem_available))
    local mem_percent=$((mem_used * 100 / mem_total))
    
    mem_total_gb=$((mem_total / 1024 / 1024))
    mem_used_gb=$((mem_used / 1024 / 1024))
    mem_avail_gb=$((mem_available / 1024 / 1024))
    
    # 系统评分计算
    local score=100
    local confirm_count=0
    local suspect_count=0
    
    # 收集异常进程信息
    local confirm_list=""
    local suspect_list=""
    local attention_list=""
    
    # 处理每个进程
    while IFS='|' read -r pid name rss vsz etime cmd; do
        [ -z "$pid" ] && continue
        
        rate=$(calculate_growth_rate "$pid" || echo "0")
        
        # 判定等级
        if [ "$rate" -ge "$THRESHOLD_CONFIRM" ]; then
            confirm_list="${confirm_list}${pid}|${name}|${rss}|${rate}|${cmd}\n"
            ((score-=20))
            ((confirm_count++))
        elif [ "$rate" -ge "$THRESHOLD_SUSPECT" ]; then
            suspect_list="${suspect_list}${pid}|${name}|${rss}|${rate}|${cmd}\n"
            ((score-=10))
            ((suspect_count++))
        elif [ "$rate" -ge "$THRESHOLD_ATTENTION" ]; then
            attention_list="${attention_list}${pid}|${name}|${rss}|${rate}|${cmd}\n"
            ((score-=5))
        fi
    done < <(tail -n +3 "$snapshot_file")
    
    # 确保分数不为负
    [ "$score" -lt 0 ] && score=0
    
    # 写入报告
    {
        echo "========== 系统内存巡检报告 =========="
        echo "巡检时间: $TIMESTAMP"
        echo "系统评分: ${score}/100 ($(get_score_desc $score))"
        echo ""
        echo "【系统概况】"
        echo "总内存: ${mem_total_gb}GB | 已用: ${mem_used_gb}GB(${mem_percent}%) | 可用: ${mem_avail_gb}GB"
        echo ""
        
        echo "【TOP 10 内存进程】"
        echo "PID    进程名              RSS(MB)   增长率(MB/h)  状态"
        echo "------ ------------------- --------- ------------- --------"
        
        # 排序输出