Alto Uso de Memoria: Diagnóstico y Análisis
Introducción
La gestión de memoria es crítica para la estabilidad y el rendimiento del servidor. Cuando los recursos de memoria se agotan, los sistemas Linux recurren al intercambio (swap), lo que degrada dramáticamente el rendimiento, o activan el asesino de Falta de Memoria (OOM killer), que termina procesos para liberar recursos. Comprender cómo diagnosticar y resolver problemas de memoria es esencial para mantener operaciones de servidor confiables.
Esta guía completa proporciona a los administradores de sistemas herramientas prácticas de línea de comandos y metodologías para diagnosticar alto uso de memoria. Aprenderás a identificar procesos que consumen mucha memoria, analizar fugas de memoria, comprender el uso de swap e implementar soluciones efectivas para problemas relacionados con la memoria.
Los problemas de memoria a menudo se manifiestan sutilmente antes de causar fallas críticas. Esta guía te enseñará a reconocer señales de advertencia tempranas, realizar análisis detallados de memoria e implementar medidas preventivas para mantener una utilización óptima de memoria en tu infraestructura Linux.
Comprendiendo la Gestión de Memoria en Linux
Tipos y Componentes de Memoria
La memoria en Linux consiste en varios componentes:
Memoria Física (RAM): Memoria de hardware real Memoria Virtual: RAM + Espacio de swap Swap: Espacio en disco utilizado como memoria extendida Caché: Caché del sistema de archivos para rendimiento Buffers: Almacenamiento temporal para operaciones de E/S Memoria Compartida: Memoria compartida entre procesos Memoria Anónima: Heap y stack de procesos
Métricas de Memoria Explicadas
Total: RAM física total instalada Usado (Used): Memoria actualmente en uso Libre (Free): Memoria completamente sin usar Disponible (Available): Memoria disponible para nuevas aplicaciones (incluye caché recuperable) Buffers: Memoria utilizada para buffers de dispositivos de bloque Caché (Cache): Memoria utilizada para caché del sistema de archivos Swap: Memoria virtual basada en disco Comprometido (Committed): Memoria asignada a procesos
Asesino OOM (Out-Of-Memory)
Cuando el sistema se queda sin memoria, el asesino OOM (Out-Of-Memory) termina procesos basándose en un sistema de puntuación:
- Los procesos que usan más memoria obtienen puntuaciones más altas
- Los procesos root obtienen puntuaciones más bajas
- Los procesos que se ejecutan más tiempo obtienen puntuaciones más bajas
- Los procesos con valores nice más bajos obtienen puntuaciones más bajas
Evaluación Inicial de Memoria
Verificación Rápida del Estado de Memoria
Comienza con estos comandos de evaluación rápida:
# Resumen básico de memoria
free -h
# Información detallada de memoria
cat /proc/meminfo
# Resumen de uso de memoria
vmstat -s
# Uso de memoria por proceso
ps aux --sort=-%mem | head -10
# Estado de memoria del sistema
cat /proc/sys/vm/swappiness
cat /proc/sys/vm/vfs_cache_pressure
# Verificar eventos OOM
dmesg | grep -i "out of memory"
grep -i "killed process" /var/log/kern.log
Interpretación rápida:
# Si disponible < 10% del total
# Y swap usado > 50%
# ENTONCES investigar inmediatamente
# Si el asesino OOM está activo
# ENTONCES escasez crítica de memoria
# Si caché > 50% y disponible bajo
# ENTONCES el caché puede recuperarse (generalmente OK)
Comprendiendo la Salida del Comando free
free -h
total used free shared buff/cache available
Mem: 15Gi 8.0Gi 1.0Gi 256Mi 6.0Gi 6.5Gi
Swap: 4.0Gi 2.0Gi 2.0Gi
Interpretación:
- Total: 16GB de RAM física
- Usado: 8GB activamente usados por procesos
- Libre: 1GB completamente sin usar
- Buff/cache: 6GB usados para caché (recuperable)
- Disponible: 6.5GB disponibles para nuevas aplicaciones
- Swap usado: 2GB (preocupante si es alto)
Punto clave: Linux usa memoria "libre" para caché para mejorar el rendimiento. Concéntrate en "disponible" no en "libre".
Paso 1: Análisis de Memoria a Nivel de Proceso
Usando ps para Análisis de Memoria
# Principales consumidores de memoria
ps aux --sort=-%mem | head -15
# Uso de memoria por nombre de proceso
ps aux | awk '{print $11, $6}' | sort -k2 -nr | head -15
# Memoria total por nombre de proceso
ps aux | awk '{arr[$11]+=$6} END {for (i in arr) print i, arr[i]}' | sort -k2 -nr
# Uso de memoria de usuario específico
ps aux | grep ^username | awk '{sum+=$6} END {print "Total:", sum/1024 "MB"}'
# Comparación VSZ vs RSS
ps aux --sort=-rss | awk '{printf "%-10s %8s %8s %s\n", $2, $5, $6, $11}' | head -15
# Uso de memoria con línea de comandos
ps -eo pid,user,pmem,rss,vsz,cmd --sort=-pmem | head -20
# Memoria de procesos hijos
ps --ppid 1234 -o pid,rss,cmd
Métricas clave:
- VSZ: Tamaño de memoria virtual (total direccionable)
- RSS: Tamaño del Conjunto Residente (RAM física usada)
- %MEM: Porcentaje de RAM física
- SHR: Memoria compartida
Comprendiendo las Columnas de Memoria
# Información detallada de memoria de proceso
ps -eo pid,user,rss,vsz,pmem,comm --sort=-rss | head -15
# VSZ en MB
ps -eo pid,user,vsz,comm --sort=-vsz | awk '{$3=int($3/1024)"M"} {print}' | head -15
# RSS en MB
ps -eo pid,user,rss,comm --sort=-rss | awk '{$3=int($3/1024)"M"} {print}' | head -15
Detalles de Memoria por Proceso
# Mapa de memoria del proceso
pmap -x 1234
# Mapa de memoria extendido
pmap -XX 1234
# Resumen del mapa de memoria
pmap -d 1234
# Uso detallado de memoria
cat /proc/1234/status | grep -i mem
cat /proc/1234/status | grep -i vm
# SMAPS para mapeo detallado
cat /proc/1234/smaps
# Estadísticas de memoria
cat /proc/1234/statm
Paso 2: Análisis de Memoria a Nivel del Sistema
Usando el Comando free
# Salida legible para humanos
free -h
# Salida en MB
free -m
# Salida en GB
free -g
# Monitoreo continuo (cada 2 segundos)
free -h -s 2
# Salida amplia
free -h -w
# Solo línea total
free -h -t
# Mostrar conteo de iteraciones
free -h -s 2 -c 10
Analizando /proc/meminfo
Información detallada de memoria del sistema:
# Ver toda la información de memoria
cat /proc/meminfo
# Métricas clave
grep -E "MemTotal|MemFree|MemAvailable|Buffers|Cached|SwapTotal|SwapFree" /proc/meminfo
# Calcular memoria realmente libre
awk '/MemAvailable/ {print "Disponible:", $2/1024/1024 "GB"}' /proc/meminfo
# Tamaño de caché
awk '/^Cached/ {print "Caché:", $2/1024/1024 "GB"}' /proc/meminfo
# Memoria slab (objetos del kernel)
awk '/^Slab/ {print "Slab:", $2/1024/1024 "GB"}' /proc/meminfo
# Verificar fugas de memoria en slab
cat /proc/slabinfo | head -20
slabtop -o
Usando vmstat
Estadísticas de memoria virtual:
# vmstat básico
vmstat 1 10
# Estadísticas específicas de memoria
vmstat -s
# Memoria activa/inactiva
vmstat -a 2 10
# Estadísticas de disco y memoria
vmstat -d 2
# Salida amplia
vmstat -w 1 5
# Columnas clave:
# swpd = swap usado
# free = memoria libre
# buff = buffers
# cache = caché
# si = swap in (desde disco)
# so = swap out (al disco)
Indicadores críticos:
# Alto swap in/out (si/so > 0 consistentemente)
# ENTONCES el sistema está en thrashing
# Memoria libre cerca de cero
# Y swap aumentando
# ENTONCES agotamiento de memoria
# bi/bo (block in/out) alto con si/so
# ENTONCES intercambio activo ocurriendo
Memoria a lo Largo del Tiempo con sar
# Instalar sysstat si es necesario
apt install sysstat
systemctl enable sysstat
# Estadísticas actuales de memoria
sar -r
# Uso de memoria con swap
sar -r -S
# Datos históricos (últimas 24 horas)
sar -r -f /var/log/sysstat/sa$(date +%d)
# Rango de tiempo específico
sar -r -s 10:00:00 -e 11:00:00
# Monitoreo en tiempo real
sar -r 2
# Estadísticas de paginación
sar -B 2
Paso 3: Análisis de Swap
Verificando el Uso de Swap
# Resumen de swap
swapon --show
# Información detallada de swap
cat /proc/swaps
# Uso de swap por proceso
for file in /proc/*/status ; do
awk '/VmSwap|Name/{printf $2 " " $3}END{ print ""}' $file
done | sort -k 2 -n -r | head -10
# Script de uso de swap
cat > /tmp/swap-usage.sh << 'EOF'
#!/bin/bash
SUM=0
OVERALL=0
for DIR in $(find /proc/ -maxdepth 1 -type d -regex "^/proc/[0-9]+")
do
PID=$(echo $DIR | cut -d / -f 3)
PROGNAME=$(ps -p $PID -o comm --no-headers)
for SWAP in $(grep VmSwap $DIR/status 2>/dev/null | awk '{print $2}')
do
((SUM=$SUM+$SWAP))
done
if [ $SUM -gt 0 ]; then
echo "$PID $PROGNAME $((SUM/1024))MB"
fi
((OVERALL=$OVERALL+$SUM))
SUM=0
done
echo "Total: $((OVERALL/1024))MB"
EOF
chmod +x /tmp/swap-usage.sh
/tmp/swap-usage.sh | sort -k3 -n -r | head -15
Análisis de Rendimiento de Swap
# Actividad de swap
vmstat 1 5 | awk '{print $7, $8}'
# Estadísticas de E/S de swap
iostat -x 1 5 | grep -A1 swap
# Monitorear swap in/out
sar -W 1 10
# Verificar configuración de swappiness
cat /proc/sys/vm/swappiness
# Valores óptimos de swappiness:
# 0-10 = Intercambio mínimo (servidores)
# 60 = Por defecto
# 100 = Intercambio agresivo
Gestión de Swap
# Verificar particiones/archivos de swap
cat /etc/fstab | grep swap
# Deshabilitar swap temporalmente
swapoff -a
# Habilitar swap
swapon -a
# Limpiar swap (requiere RAM suficiente)
swapoff -a && swapon -a
# Agregar archivo de swap temporal
dd if=/dev/zero of=/swapfile bs=1M count=2048
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
# Verificar
swapon --show
# Eliminar archivo de swap
swapoff /swapfile
rm /swapfile
Paso 4: Detección de Fugas de Memoria
Identificando Fugas de Memoria
Las fugas de memoria ocurren cuando los procesos consumen continuamente memoria sin liberarla:
# Monitorear memoria del proceso a lo largo del tiempo
while true; do
ps -p 1234 -o pid,rss,vsz,cmd
sleep 60
done
# Script automatizado de detección de fugas
cat > /tmp/memory-leak-detector.sh << 'EOF'
#!/bin/bash
PID=$1
LOG="/tmp/memleak-$PID.log"
echo "Monitoreando PID $PID en busca de fugas de memoria..."
echo "Tiempo,RSS_KB,VSZ_KB" > $LOG
while kill -0 $PID 2>/dev/null; do
TIMESTAMP=$(date +%H:%M:%S)
RSS=$(ps -p $PID -o rss= | tr -d ' ')
VSZ=$(ps -p $PID -o vsz= | tr -d ' ')
echo "$TIMESTAMP,$RSS,$VSZ" >> $LOG
sleep 300 # Verificar cada 5 minutos
done
echo "Proceso terminado. Verifica $LOG para tendencias"
EOF
chmod +x /tmp/memory-leak-detector.sh
/tmp/memory-leak-detector.sh 1234
Analizando Fugas de Memoria
# Verificar tendencia de crecimiento de memoria
cat /tmp/memleak-1234.log | awk -F',' '{print $2}' |
while read value; do
echo $value
done
# Usar valgrind para detección de fugas
valgrind --leak-check=full --show-leak-kinds=all command
# Valgrind con registro
valgrind --leak-check=full --log-file=valgrind.log command
# Verificar fragmentación de memoria
cat /proc/buddyinfo
# Fugas del asignador slab
slabtop -o -s c
watch -n 1 "cat /proc/slabinfo | grep dentry"
Detección de Fugas Específicas de Aplicación
# Fuga de memoria PHP
# Agregar a php.ini
memory_limit = 256M
display_errors = On
log_errors = On
# Monitorear proceso PHP
watch -n 5 'ps aux | grep php-fpm | grep -v grep'
# Volcado de heap Java
jmap -dump:live,format=b,file=heap.bin PID
jmap -heap PID
# Perfilado de memoria Python
python -m memory_profiler script.py
# Instantánea de heap Node.js
node --inspect script.js
# Usar Chrome DevTools para análisis de heap
Paso 5: Análisis de Caché y Buffer
Comprendiendo el Caché
Linux almacena en caché agresivamente para mejorar el rendimiento:
# Tamaño de caché
free -h | grep "Mem:" | awk '{print $6}'
# Limpiar caché de página (seguro)
sync
echo 1 > /proc/sys/vm/drop_caches
# Limpiar dentries e inodes
echo 2 > /proc/sys/vm/drop_caches
# Limpiar todos los cachés
echo 3 > /proc/sys/vm/drop_caches
# Nota: El caché se limpia automáticamente cuando se necesita memoria
# La limpieza manual rara vez es necesaria
# Configuración de presión de caché
cat /proc/sys/vm/vfs_cache_pressure
# Por defecto: 100
# Más bajo: Preferir mantener caché
# Más alto: Recuperar caché más agresivamente
Análisis de Buffer
# Tamaño de buffer
awk '/^Buffers:/ {print "Buffers:", $2/1024 "MB"}' /proc/meminfo
# Qué está usando buffers
cat /proc/sys/vm/block_dump
echo 1 > /proc/sys/vm/block_dump
dmesg | tail -100
echo 0 > /proc/sys/vm/block_dump # Deshabilitar
# Estadísticas de buffer de E/S
vmstat -a 1 5
Paso 6: Análisis OOM
Detectando Eventos OOM
# Verificar muertes OOM recientes
dmesg | grep -i "killed process"
dmesg | grep -i "out of memory"
# Registro del kernel
grep -i "out of memory" /var/log/kern.log
grep -i "oom" /var/log/messages
# Registros del journal
journalctl -k | grep -i "out of memory"
journalctl --since "1 hour ago" | grep -i oom
# Obtener puntuaciones OOM para procesos en ejecución
for proc in /proc/*/oom_score; do
printf "%d %s\n" "$(cat $proc)" "$(cat ${proc%/*}/cmdline | tr '\0' ' ')"
done | sort -nr | head -15
Análisis de Puntuación OOM
# Verificar puntuación OOM para proceso
cat /proc/1234/oom_score
cat /proc/1234/oom_score_adj
# Establecer ajuste OOM (-1000 a 1000)
echo -500 > /proc/1234/oom_score_adj
# Proteger proceso crítico del asesino OOM
echo -1000 > /proc/1234/oom_score_adj
# Hacer que el proceso sea más probable de ser eliminado
echo 1000 > /proc/1234/oom_score_adj
# Establecer permanentemente vía systemd
cat > /etc/systemd/system/critical-service.service.d/oom.conf << 'EOF'
[Service]
OOMScoreAdjust=-1000
EOF
systemctl daemon-reload
systemctl restart critical-service
Previniendo OOM
# Configurar vm.panic_on_oom
cat /proc/sys/vm/panic_on_oom
# 0 = El asesino OOM mata el proceso
# 1 = El kernel entra en pánico con OOM
# 2 = Siempre entrar en pánico
# Habilitar muerte OOM para cgroup
cat /proc/sys/vm/oom_kill_allocating_task
# Configuraciones de sobrecompromiso de memoria
cat /proc/sys/vm/overcommit_memory
# 0 = Heurístico (por defecto)
# 1 = Siempre sobrecomprometer
# 2 = Nunca sobrecomprometer más allá del límite
# Establecer ratio de sobrecompromiso
cat /proc/sys/vm/overcommit_ratio
echo 80 > /proc/sys/vm/overcommit_ratio
Soluciones y Remediación
Acciones Inmediatas
Identificar y matar proceso que consume mucha memoria:
# Encontrar mayor consumidor de memoria
ps aux --sort=-%mem | head -2 | tail -1 | awk '{print $2}'
# Matarlo
kill $(ps aux --sort=-%mem | head -2 | tail -1 | awk '{print $2}')
# Forzar muerte si es necesario
kill -9 $(ps aux --sort=-%mem | head -2 | tail -1 | awk '{print $2}')
Limpiar caché para liberar memoria:
# Sincronizar y limpiar
sync && echo 3 > /proc/sys/vm/drop_caches
# Verificar memoria liberada
free -h
Reiniciar servicio problemático:
# Reiniciar servicio
systemctl restart service-name
# Verificar memoria después del reinicio
ps aux | grep service-name
Agregando Más Swap
# Crear archivo de swap de 4GB
dd if=/dev/zero of=/swapfile bs=1G count=4
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
# Hacer permanente
echo '/swapfile none swap sw 0 0' >> /etc/fstab
# Verificar
swapon --show
free -h
Optimizando Swappiness
# Verificar valor actual
cat /proc/sys/vm/swappiness
# Establecer swappiness (0-100)
# Más bajo = menos intercambio
sysctl vm.swappiness=10
# Hacer permanente
echo "vm.swappiness=10" >> /etc/sysctl.conf
# Aplicar inmediatamente
sysctl -p
# Valores recomendados:
# Escritorio: 60 (por defecto)
# Servidor: 10
# Servidor de base de datos: 1
Límites de Memoria de Aplicación
Límites de servicio systemd:
cat > /etc/systemd/system/service-name.service.d/memory.conf << 'EOF'
[Service]
MemoryLimit=2G
MemoryMax=2G
EOF
systemctl daemon-reload
systemctl restart service-name
Límites de memoria de Cgroup:
# Crear cgroup
cgcreate -g memory:/myapp
# Establecer límite (2GB)
echo 2147483648 > /sys/fs/cgroup/memory/myapp/memory.limit_in_bytes
# Ejecutar proceso en cgroup
cgexec -g memory:myapp command
Límite de memoria PHP:
# Editar php.ini
memory_limit = 256M
# Anulación por script
php -d memory_limit=512M script.php
Optimización de memoria MySQL:
# Editar /etc/mysql/my.cnf
[mysqld]
innodb_buffer_pool_size = 4G
key_buffer_size = 256M
max_connections = 100
table_open_cache = 2000
Ajuste de Parámetros del Kernel
# Editar /etc/sysctl.conf
# Swappiness
vm.swappiness = 10
# Presión de caché
vm.vfs_cache_pressure = 50
# Configuraciones de sobrecompromiso
vm.overcommit_memory = 0
vm.overcommit_ratio = 80
# Ratio dirty (% de RAM para páginas dirty)
vm.dirty_ratio = 15
vm.dirty_background_ratio = 5
# Memoria libre mínima (KB)
vm.min_free_kbytes = 65536
# Aplicar cambios
sysctl -p
Prevención y Monitoreo
Monitoreo Continuo de Memoria
cat > /usr/local/bin/memory-monitor.sh << 'EOF'
#!/bin/bash
THRESHOLD=90
LOG_FILE="/var/log/memory-monitor.log"
ALERT_EMAIL="[email protected]"
while true; do
TOTAL=$(free | grep Mem | awk '{print $2}')
USED=$(free | grep Mem | awk '{print $3}')
PERCENT=$((USED * 100 / TOTAL))
if [ $PERCENT -gt $THRESHOLD ]; then
echo "$(date): Alto uso de memoria: $PERCENT%" >> "$LOG_FILE"
echo "Principales procesos:" >> "$LOG_FILE"
ps aux --sort=-%mem | head -10 >> "$LOG_FILE"
# Enviar alerta
echo "Alerta de memoria alta en $(hostname): $PERCENT%" | \
mail -s "Alerta de Memoria: $PERCENT%" "$ALERT_EMAIL"
fi
sleep 300 # Verificar cada 5 minutos
done
EOF
chmod +x /usr/local/bin/memory-monitor.sh
# Ejecutar como servicio systemd
cat > /etc/systemd/system/memory-monitor.service << 'EOF'
[Unit]
Description=Servicio de Monitoreo de Memoria
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/memory-monitor.sh
Restart=always
[Install]
WantedBy=multi-user.target
EOF
systemctl enable memory-monitor.service
systemctl start memory-monitor.service
Informes de Uso de Memoria
cat > /usr/local/bin/memory-report.sh << 'EOF'
#!/bin/bash
REPORT="/tmp/memory-report-$(date +%Y%m%d).txt"
echo "Informe de Uso de Memoria - $(date)" > "$REPORT"
echo "================================" >> "$REPORT"
echo "" >> "$REPORT"
echo "Memoria del Sistema:" >> "$REPORT"
free -h >> "$REPORT"
echo "" >> "$REPORT"
echo "Uso de Swap:" >> "$REPORT"
swapon --show >> "$REPORT"
echo "" >> "$REPORT"
echo "Principales 15 Consumidores de Memoria:" >> "$REPORT"
ps aux --sort=-%mem | head -16 >> "$REPORT"
echo "" >> "$REPORT"
echo "Memoria por Usuario:" >> "$REPORT"
ps aux | awk '{arr[$1]+=$6} END {for (i in arr) print i, int(arr[i]/1024) "MB"}' | sort -k2 -nr >> "$REPORT"
echo "" >> "$REPORT"
echo "Eventos OOM Recientes:" >> "$REPORT"
dmesg | grep -i "killed process" | tail -5 >> "$REPORT"
mail -s "Informe Diario de Memoria - $(hostname)" [email protected] < "$REPORT"
EOF
chmod +x /usr/local/bin/memory-report.sh
# Programar diariamente a las 8 AM
echo "0 8 * * * /usr/local/bin/memory-report.sh" | crontab -
Línea Base de Rendimiento
cat > /usr/local/bin/memory-baseline.sh << 'EOF'
#!/bin/bash
BASELINE_DIR="/var/log/memory-baseline"
mkdir -p "$BASELINE_DIR"
DATE=$(date +%Y%m%d-%H%M%S)
free -h > "$BASELINE_DIR/free-$DATE.txt"
cat /proc/meminfo > "$BASELINE_DIR/meminfo-$DATE.txt"
ps aux --sort=-%mem | head -50 > "$BASELINE_DIR/processes-$DATE.txt"
vmstat -s > "$BASELINE_DIR/vmstat-$DATE.txt"
echo "Línea base de memoria capturada: $DATE"
EOF
chmod +x /usr/local/bin/memory-baseline.sh
# Ejecutar semanalmente
echo "0 2 * * 0 /usr/local/bin/memory-baseline.sh" | crontab -
Diagnóstico Avanzado
Usando smem
Herramienta avanzada de reporte de memoria:
# Instalar smem
apt install smem
# Uso de memoria por proceso
smem -t
# Uso de memoria ordenado
smem -s pss -r
# Memoria por usuario
smem -u
# Resumen a nivel del sistema
smem -w
# Mapa de memoria
smem -m
# Vista de porcentaje
smem -p
Perfilado de Memoria
# Usar memusage para programas en C
memusage command
# Generar gráfico de memoria
memusage --png=output.png command
# Massif (herramienta Valgrind)
valgrind --tool=massif command
ms_print massif.out.*
Conclusión
La gestión de memoria es crucial para la estabilidad y el rendimiento del sistema. Puntos clave:
- Monitorear disponible, no libre: Linux almacena en caché agresivamente
- Vigilar el uso de swap: Alto swap indica presión de memoria
- Identificar fugas de memoria temprano: Monitorear tendencias a lo largo del tiempo
- Configurar puntuaciones OOM: Proteger procesos críticos
- Ajustar parámetros del kernel: Optimizar para tu carga de trabajo
- Establecer límites de proceso: Prevenir consumo descontrolado de memoria
- Monitorear continuamente: El monitoreo automatizado detecta problemas temprano
El monitoreo regular, la configuración adecuada y la comprensión de estas herramientas de diagnóstico te ayudarán a mantener una utilización óptima de memoria y prevenir interrupciones relacionadas con la memoria. Mantén estos comandos fácilmente disponibles para solución rápida de problemas cuando ocurran problemas de memoria.


