内存不足/OOM问题

title

内存不足(Out of Memory, OOM)是Linux系统中严重的问题,会导致系统杀死进程、服务中断甚至系统崩溃。

🚨 问题现象

常见错误信息

# OOM Killer消息
Out of memory: Kill process
oom-killer: gfp_mask=0x201da, order=0, oom_score_adj=0
Killed process [PID] (process_name) total-vm:[memory]kB

# 内存分配失败
Cannot allocate memory
fork: retry: Resource temporarily unavailable

# 应用程序错误
java.lang.OutOfMemoryError
malloc: Cannot allocate memory

系统表现

  • 系统响应极慢
  • 进程被突然杀死
  • 新进程无法启动
  • 服务异常退出
  • 系统频繁使用swap
  • 登录困难或失败

🔍 问题诊断

1. 检查内存使用情况

# 查看内存使用状态
free -h
cat /proc/meminfo

# 查看内存使用详情
vmstat 1 5
sar -r 1 5

# 查看swap使用情况
swapon -s
cat /proc/swaps

2. 查看OOM日志

# 查看OOM事件
dmesg | grep -i "killed process"
grep -i "out of memory\|oom-killer" /var/log/messages
journalctl --since "1 hour ago" | grep -i oom

# 查看最近被杀死的进程
dmesg | grep "Killed process" | tail -10

3. 识别内存占用大户

# 按内存使用排序进程
ps aux --sort=-%mem | head -20
top -o %MEM

# 查看进程内存映射
pmap -x PID
cat /proc/PID/smaps

# 查看系统内存分布
cat /proc/meminfo | grep -E "(MemTotal|MemFree|MemAvailable|Buffers|Cached|SwapTotal|SwapFree)"

4. 检查内存泄漏

# 监控进程内存使用趋势
while true; do
    date
    ps aux | grep process_name | grep -v grep
    sleep 60
done > memory_trend.log

# 使用valgrind检查内存泄漏
valgrind --tool=memcheck --leak-check=full ./program

5. 分析OOM分数

# 查看进程OOM分数
for pid in $(ps -eo pid --no-headers); do
    if [ -f /proc/$pid/oom_score ]; then
        echo "PID: $pid, OOM Score: $(cat /proc/$pid/oom_score), Command: $(ps -p $pid -o comm --no-headers)"
    fi
done | sort -k4 -nr | head -10

🛠️ 解决方案

立即释放内存

1. 清理系统缓存

# 同步磁盘缓存到硬盘
sync

# 清理页面缓存
echo 1 > /proc/sys/vm/drop_caches

# 清理目录项和inode缓存
echo 2 > /proc/sys/vm/drop_caches

# 清理所有缓存
echo 3 > /proc/sys/vm/drop_caches

# 重新启用缓存
echo 0 > /proc/sys/vm/drop_caches

2. 终止占用内存的进程

# 找出内存占用最大的进程
PID=$(ps aux --sort=-%mem | head -2 | tail -1 | awk '{print $2}')
echo "最大内存占用进程PID: $PID"

# 优雅终止进程
kill -TERM $PID

# 强制终止进程(如果优雅终止失败)
kill -KILL $PID

# 批量终止某类进程
pkill -f process_name

3. 重启服务释放内存

# 重启占用内存较多的服务
sudo systemctl restart mysql
sudo systemctl restart apache2
sudo systemctl restart nginx

# 重启后检查内存使用
free -h

调整OOM Killer行为

1. 配置OOM分数

# 查看进程的oom_score_adj值
cat /proc/PID/oom_score_adj

# 降低重要进程被杀死的概率
echo -1000 > /proc/PID/oom_score_adj  # 完全禁用OOM killer
echo -500 > /proc/PID/oom_score_adj   # 降低被杀概率

# 提高不重要进程被杀死的概率
echo 500 > /proc/PID/oom_score_adj
echo 1000 > /proc/PID/oom_score_adj   # 最高优先级被杀

2. 系统级OOM配置

# 配置OOM处理策略
echo 2 > /proc/sys/vm/overcommit_memory  # 不允许过量分配
echo 50 > /proc/sys/vm/overcommit_ratio  # 过量分配比例

# 永久设置
echo "vm.overcommit_memory = 2" >> /etc/sysctl.conf
echo "vm.overcommit_ratio = 50" >> /etc/sysctl.conf
sysctl -p

3. 服务级别OOM保护

# 在systemd服务文件中设置OOM保护
sudo vim /etc/systemd/system/critical-service.service

[Unit]
Description=Critical Service
After=network.target

[Service]
Type=simple
ExecStart=/path/to/critical-service
OOMScoreAdjust=-1000  # 保护关键服务
MemoryMax=1G          # 限制最大内存使用

[Install]
WantedBy=multi-user.target

# 重载并重启服务
sudo systemctl daemon-reload
sudo systemctl restart critical-service

内存优化配置

1. 调整swap策略

# 查看当前swappiness值
cat /proc/sys/vm/swappiness

# 临时调整swappiness
echo 10 > /proc/sys/vm/swappiness

# 永久设置
echo "vm.swappiness = 10" >> /etc/sysctl.conf

# 增加swap空间
sudo dd if=/dev/zero of=/swapfile bs=1G count=4
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

# 永久启用swap
echo "/swapfile none swap sw 0 0" >> /etc/fstab

2. 优化内存回收

# 配置内存回收参数
echo "vm.min_free_kbytes = 65536" >> /etc/sysctl.conf
echo "vm.vfs_cache_pressure = 50" >> /etc/sysctl.conf
echo "vm.dirty_ratio = 15" >> /etc/sysctl.conf
echo "vm.dirty_background_ratio = 5" >> /etc/sysctl.conf

# 应用配置
sysctl -p

🔧 内存监控脚本

OOM监控脚本

#!/bin/bash
# oom_monitor.sh

LOG_FILE="/var/log/oom_monitor.log"
EMAIL="admin@example.com"
MEMORY_THRESHOLD=90  # 内存使用率阈值

log_message() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> $LOG_FILE
}

# 检查内存使用率
check_memory_usage() {
    MEM_TOTAL=$(free | grep Mem | awk '{print $2}')
    MEM_USED=$(free | grep Mem | awk '{print $3}')
    MEM_USAGE=$(echo "scale=2; $MEM_USED * 100 / $MEM_TOTAL" | bc)
    echo $MEM_USAGE
}

# 检查是否发生OOM
check_oom_events() {
    OOM_COUNT=$(dmesg | grep -c "Killed process")
    echo $OOM_COUNT
}

# 获取内存占用TOP进程
get_top_memory_processes() {
    ps aux --sort=-%mem | head -6 | tail -5
}

# 主监控逻辑
MEMORY_USAGE=$(check_memory_usage)
OOM_COUNT=$(check_oom_events)

log_message "内存使用率: ${MEMORY_USAGE}%"

# 检查内存使用率
if (( $(echo "$MEMORY_USAGE > $MEMORY_THRESHOLD" | bc -l) )); then
    log_message "警告: 内存使用率过高 ${MEMORY_USAGE}%"

    # 记录TOP进程
    log_message "TOP内存进程:"
    get_top_memory_processes >> $LOG_FILE

    # 发送告警邮件
    cat << EOF | mail -s "内存使用率告警" $EMAIL
内存使用率: ${MEMORY_USAGE}%
时间: $(date)

TOP内存进程:
$(get_top_memory_processes)

建议检查系统内存使用情况
EOF
fi

# 检查OOM事件
LAST_OOM_COUNT_FILE="/tmp/last_oom_count"
if [ -f "$LAST_OOM_COUNT_FILE" ]; then
    LAST_OOM_COUNT=$(cat $LAST_OOM_COUNT_FILE)
else
    LAST_OOM_COUNT=0
fi

if [ "$OOM_COUNT" -gt "$LAST_OOM_COUNT" ]; then
    NEW_OOM_EVENTS=$((OOM_COUNT - LAST_OOM_COUNT))
    log_message "检测到 $NEW_OOM_EVENTS 个新的OOM事件"

    # 记录最近的OOM事件
    dmesg | grep "Killed process" | tail -$NEW_OOM_EVENTS >> $LOG_FILE

    # 发送紧急告警
    echo "检测到OOM事件,有 $NEW_OOM_EVENTS 个进程被杀死" | mail -s "OOM紧急告警" $EMAIL
fi

echo $OOM_COUNT > $LAST_OOM_COUNT_FILE

内存清理脚本

#!/bin/bash
# memory_cleanup.sh

LOG_FILE="/var/log/memory_cleanup.log"

log_message() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> $LOG_FILE
}

# 清理前记录内存状态
log_message "清理前内存状态:"
free -h >> $LOG_FILE

# 1. 清理系统缓存
log_message "清理系统缓存..."
sync
echo 3 > /proc/sys/vm/drop_caches

# 2. 清理临时文件
log_message "清理临时文件..."
find /tmp -type f -atime +1 -delete 2>/dev/null
find /var/tmp -type f -atime +1 -delete 2>/dev/null

# 3. 清理日志文件
log_message "清理大日志文件..."
find /var/log -name "*.log" -size +100M -exec truncate -s 50M {} \; 2>/dev/null

# 4. 重启占用内存较多的服务
MEMORY_THRESHOLD=1000000  # 1GB in KB

for service in apache2 nginx mysql postgresql; do
    PID=$(pgrep $service | head -1)
    if [ -n "$PID" ]; then
        MEMORY_KB=$(ps -o pid,vsz --no-headers -p $PID | awk '{print $2}')
        if [ "$MEMORY_KB" -gt "$MEMORY_THRESHOLD" ]; then
            log_message "重启高内存使用服务: $service (内存使用: ${MEMORY_KB}KB)"
            systemctl restart $service 2>/dev/null
        fi
    fi
done

# 清理后记录内存状态
log_message "清理后内存状态:"
free -h >> $LOG_FILE

log_message "内存清理完成"

自动OOM恢复脚本

#!/bin/bash
# oom_recovery.sh

# 检测到OOM后的自动恢复脚本
RECOVERY_LOG="/var/log/oom_recovery.log"

log_recovery() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> $RECOVERY_LOG
}

# 检查是否发生了OOM
if dmesg | tail -50 | grep -q "Killed process"; then
    log_recovery "检测到OOM事件,开始自动恢复..."

    # 1. 清理内存
    sync
    echo 3 > /proc/sys/vm/drop_caches
    log_recovery "已清理系统缓存"

    # 2. 检查关键服务状态
    CRITICAL_SERVICES=("sshd" "network" "systemd-logind")

    for service in "${CRITICAL_SERVICES[@]}"; do
        if ! systemctl is-active --quiet $service; then
            log_recovery "重启关键服务: $service"
            systemctl start $service
        fi
    done

    # 3. 调整swap
    if [ $(swapon -s | wc -l) -eq 1 ]; then
        log_recovery "系统无swap,尝试创建临时swap"
        if [ $(df / | tail -1 | awk '{print $4}') -gt 1048576 ]; then
            dd if=/dev/zero of=/tmp/emergency_swap bs=1M count=512 2>/dev/null
            chmod 600 /tmp/emergency_swap
            mkswap /tmp/emergency_swap >/dev/null 2>&1
            swapon /tmp/emergency_swap 2>/dev/null
            log_recovery "已创建512MB临时swap"
        fi
    fi

    # 4. 发送通知
    echo "系统发生OOM,已执行自动恢复操作" | mail -s "OOM自动恢复通知" admin@example.com

    log_recovery "OOM自动恢复完成"
fi

📊 内存管理最佳实践

1. 预防性监控

# 添加定时监控
echo "*/5 * * * * /usr/local/bin/oom_monitor.sh" | crontab -

# 设置内存告警阈值
echo "*/10 * * * * /usr/local/bin/memory_cleanup.sh" | crontab -

2. 应用程序优化

# Java应用内存限制
export JAVA_OPTS="-Xmx2g -Xms1g -XX:+UseG1GC"

# 数据库内存配置
# MySQL配置示例
[mysqld]
innodb_buffer_pool_size = 1G
query_cache_size = 128M
tmp_table_size = 64M
max_heap_table_size = 64M

3. 系统配置优化

# 编辑/etc/security/limits.conf
* soft memlock unlimited
* hard memlock unlimited
* soft as unlimited
* hard as unlimited

# 配置systemd服务内存限制
[Service]
MemoryMax=2G
MemorySwapMax=1G

🚨 应急处理

系统内存严重不足时

  1. 立即释放内存 ```bash

    清理所有缓存

    echo 3 > /proc/sys/vm/drop_caches

终止非关键进程

pkill -f chrome pkill -f firefox


2. **创建紧急swap**
```bash
dd if=/dev/zero of=/emergency_swap bs=1M count=1024
mkswap /emergency_swap
swapon /emergency_swap
  1. 重启关键服务
    systemctl restart systemd-logind
    systemctl restart sshd
    

📚 相关工具

  • htop - 交互式进程查看器
  • iotop - I/O监控工具
  • valgrind - 内存调试工具
  • smem - 内存使用报告工具
  • pmap - 进程内存映射查看器
  • vmstat - 虚拟内存统计

通过合理的内存管理和监控,可以有效预防和处理OOM问题,确保系统稳定运行。

内存管理是Linux系统性能优化的重要方面,理解OOM机制对系统稳定性非常重要。


powered by Gitbook© 2025 编外计划 | 最后修改: 2025-07-28 12:47:16

results matching ""

    No results matching ""