...

Entender el OOM Killer: cuando Linux termina procesos

El artículo explica cómo el oom killer linux interviene ante cuellos de botella agudos en la memoria RAM y por qué cierra los servicios de forma forzada para mantener el servidor operativo. Te mostraré paso a paso cómo identifico los desencadenantes, comprendo el sistema de puntuación y controlo el comportamiento ante la presión de la memoria mediante ajustes específicos.

Puntos centrales

  • Disparador: Los eventos OOM se producen cuando la RAM está llena, el espacio de intercambio se ha agotado y los intentos de recuperación han fallado.
  • Puntuación: El núcleo asigna un oom_score y elimina los procesos que consumen mucha memoria.
  • Reconocimiento: Se puede encontrar más información en dmesg con „Out of memory“ y „Killed process“.
  • Sistema de controlCon oom_score_adj Priorizo los servicios de forma específica según su importancia.
  • Prevención: La supervisión, los límites, la estrategia de intercambio y el análisis de fugas evitan las muertes brutales.

¿Qué es el «OOM Killer» del núcleo de Linux?

El OOM Killer es el último Línea de seguridad del núcleo y finaliza los procesos cuando ya no quedan páginas de memoria disponibles. Lo considero una parada de emergencia controlada que evita un fallo total y libera memoria RAM de forma inmediata. Antes, el sistema intenta intercambiar páginas o vaciar las cachés, pero si la presión persiste, solo queda la interrupción brusca. En los registros, reconozco este momento por entradas como „Out of memory“ y „Killed process“, a menudo acompañadas de un SIGKILL. Este comportamiento está activado de forma predeterminada y se ejecuta automáticamente, lo que a menudo provoca sorpresas en entornos de producción cuando servicios importantes desaparecen sin previo aviso.

¿Cuándo se activa el mecanismo?

Los eventos OOM se producen cuando la memoria RAM física está casi completamente ocupada, el espacio de intercambio ya no ofrece capacidad de almacenamiento y las solicitudes de nuevas páginas fallan. En esta situación, el núcleo evalúa si es posible recuperar espacio del Caché de página y el intercambio de memoria sigue siendo útil, y solo activa el «killer» cuando la situación es desesperada. Los picos a corto plazo no activan directamente el mecanismo; se necesita una presión sostenida e intentos infructuosos de liberación. Para el análisis, me resulta útil examinar el RSS, el uso de la memoria de intercambio, la proporción de caché de página y las asignaciones por grupo de control. Quien quiera comprender más a fondo cómo funciona el desplazamiento de la caché, puede consultar los antecedentes sobre Eliminación de la caché de página verlos y medir los efectos en el propio sistema.

¿Cómo elige el núcleo a la „víctima“?

La selección se realiza según un Puntuación, que utiliza Linux como oom_score se calcula por cada proceso. Una alta proporción de la memoria total, un valor elevado de RSS y un rol no crítico dan lugar a una puntuación más alta. Los procesos cercanos al sistema, como init, reciben una bonificación, mientras que los procesos de trabajo que consumen mucha memoria o las cachés suelen situarse en cabeza. A través de oom_score_adj puedo modificar la puntuación de forma selectiva y, con ello, controlar la cadena de víctimas. Normalmente, el núcleo termina el proceso con la puntuación más alta mediante SIGKILL para liberar la mayor cantidad posible de RAM de una sola vez.

Detectar indicios: registros y señales

Tras un cese repentino del servicio, lo primero que compruebo es dmesg y los registros del núcleo. Si aparecen mensajes como „Out of memory“ y „Killed process“, anoto el PID, el nombre del proceso, el usuario y la puntuación calculada. A menudo, la propia aplicación carece de un registro de errores, ya que SIGKILL no permite una fase de limpieza. Comparo el momento con los datos de monitorización para comprender el aumento de la RAM, el swap y el RSS por proceso. De este modo, identifico fugas, montones (heaps) demasiado grandes o límites ausentes de forma fiable y rápida.

Toma el control con oom_score y oom_score_adj

Cada proceso tiene en /proc/[PID]/oom_score un actual Valor por su situación de riesgo. Con /proc/[PID]/oom_score_adj Reduzco o aumento la probabilidad de que el kernel termine este proceso. Protejo los servicios críticos, como las bases de datos, con un ajuste negativo (adj), mientras que los procesos de trabajo menos importantes los configuro como „sacrificables“ con un ajuste positivo (adj). El cambio surte efecto de inmediato, lo que resulta especialmente útil durante los despliegues o las pruebas de carga. De este modo, convierto un mecanismo de emergencia impredecible en una herramienta que se ajusta a mis prioridades.

Casos típicos de alojamiento web con limitaciones de almacenamiento

En entornos con muchos contenedores, bases de datos y cachés, veo el OOM Killer con especial frecuencia. Una base de datos que crece sin control desplaza a otros servicios del RAM y provoca fallos graves. Las fugas de memoria en las aplicaciones web van acumulando presión a lo largo de las horas, hasta que ya no sirve de nada la recuperación de memoria. Unos límites de contenedores demasiado generosos en un servidor demasiado pequeño agravan aún más la situación. Quien conozca estos patrones, activará alertas a tiempo e intervendrá antes de que el «asesino» tome el control.

Buenas prácticas para evitar las muertes brutales

Planifico el almacenamiento de forma realista, incluyo un margen para los picos de carga y establezco límites claros por servicio. El sistema de monitorización registra, por cada proceso, el RSS, el uso de swap y oom_score, para que se activen las alertas antes de que se produzca una situación de emergencia. En entornos de contenedores, establezco límites de cgroups para que los servicios individuales no acaparen los recursos del host. Una estrategia de swap adecuada absorbe los picos de carga sin ralentizar el sistema de forma permanente. Para comprender mejor el tema y planificarlo adecuadamente, me ayudo de guías prácticas sobre Gestionar la memoria virtual, para que las cargas de trabajo dispongan de recursos suficientes de forma fiable.

Procedimiento estructurado ante un incidente de OOM

Tras el incidente, lo primero que hago es recuperar las entradas del registro dmesg y kern.log, y los ordeno cronológicamente. En la supervisión, compruebo las curvas de RAM, swap, RSS y caché de páginas para ver la evolución de la carga. A continuación, compruebo los límites de ulimits, Cgroup y contenedores, así como los parámetros de las aplicaciones, como los montones de las JVM. Después, ajusto oom_score_adj para que lo más importante se mantenga y lo prescindible se elimine primero. Por último, soluciono la causa: subsano las fugas, limito las cachés, reduzco el paralelismo y dimensiono correctamente las capacidades.

Características específicas de los entornos VPS y en la nube

En las máquinas virtuales se añade un segundo nivel de límites, por ejemplo, a través del hipervisor o la orquestación. Por eso conozco la asignación de RAM-Establece la cantidad exacta y configura adecuadamente los límites de Kubernetes o de los contenedores. El OOM Killer de Linux sigue funcionando tal y como se ha descrito, pero los mecanismos del proveedor pueden provocar restricciones adicionales. Precisamente cuando hay muchos pods, resulta útil establecer una priorización clara: las implementaciones importantes reciben reservas, mientras que los trabajos no críticos se ejecutan con menos recursos. La documentación del proveedor de la plataforma y las pruebas propias evitan sorpresas en el entorno de producción.

Ajuste preciso de la memoria: overcommit, swappiness y cachés

Quien quiera controlar los riesgos de OOM debe ajustar los parámetros del núcleo con prudencia y comprender cómo interactúan entre sí. vm.overcommit_memory y vm.overcommit_ratio controlar el grado de generosidad con el que Linux permite las asignaciones virtuales, mientras que vm.swappiness influye en la relación entre el intercambio y la recuperación. vm.vfs_cache_pressure regula el nivel de agresividad a la hora de liberar las cachés de inodos y dentry, lo que influye directamente en el margen de maniobra de la caché de páginas. Siempre compruebo los efectos en condiciones realistas Carga, registra las métricas y realiza los cambios de forma gradual. Para conocer los antecedentes y los escenarios, resulta útil consultar Sobrecarga de memoria, para elegir adecuadamente los valores predeterminados propios.

Parámetro/Indicador Función en el sistema Dónde realizar la prueba Dirección habitual
RAM libre Un colchón frente a las muertes difíciles free, /proc/meminfo Mantener suficientes reservas
Uso del swap Amortiguadores para picos free, vmstat Bajo a moderado
vm.overcommit_memory Asignación virtual sysctl 0/2 según el riesgo
vm.overcommit_ratio Cuota para el overcommit sysctl Adaptado a la carga de trabajo
vm.swappiness Propensión al swap sysctl El valor medio en lugar del extremo
vm.vfs_cache_pressure Recuperación de la memoria caché de VFS sysctl 100 como punto de partida

OOM global frente a OOM de Cgroup: ¿qué es lo que se cierra exactamente?

En configuraciones modernas con Cgroups (v1/v2), un evento OOM puede local en un cgroup de memoria o global que se ejecute en el servidor. Si un proceso se ejecuta en un contenedor con memoria.max (o límite), el núcleo suele limitarse a terminar solo los procesos de ese Cgroup („memcg OOM“), mientras que el resto del sistema sigue funcionando. En dmesg Lo reconozco por indicios como constraint=CONSTRAINT_MEMCG o indicaciones sobre el Cgroup en cuestión. Solo cuando ningún Cgroup pueda ceder más RAM y se haya agotado la memoria global, interviene el a nivel del sistema OOM Killer. Para garantizar la estabilidad, me parece importante establecer los límites de tal forma que un servicio que se salga de los límites falle dentro de su propio Cgroup, en lugar de afectar a todo el host. En Cgroups v2, además, puedo utilizar memoria.alta establecer limitaciones suaves y con memory.oom.group Establecer que, en caso de emergencia, todo el grupo se detenga: es más limpio que dejar un proceso residual a medio funcionar.

Herramientas y métricas en la práctica

Para identificar rápidamente las causas, recopilo datos cuantitativos reproducibles. Estas herramientas me resultan de gran ayuda habitualmente:

  • Resumen del proceso: ps -eo pid,ppid,cmd,%mem,rss --sort=-rss | head muestra los procesos que consumen más memoria.
  • Resumen de Smaps: cat /proc//smaps_rollup Proporciona los valores RSS/PSS/Swap de un proceso sin necesidad de un análisis prolongado.
  • pmap: pmap -x | sort -nrk3 | head Enumera las asignaciones con tamaño y RSS, ideal para montones y segmentos grandes.
  • Uso de losas: slabtop -o muestra las cachés del núcleo, que pueden aumentar de tamaño cuando la carga es elevada.
  • Presión del sistema: vmstat 1 y sar -r 1 proporcionan contexto sobre la paginación, las operaciones de E/S de intercambio y las liberaciones de memoria.
  • Estadísticas de cgroup: En la v2 compruebo /sys/fs/cgroup/memory.current, memory.swap.current y memory.stat del servicio en cuestión.
Leer fácilmente los registros OOM de #
dmesg -T | egrep -i 'out of memory|oom-kill|killed process'

# Ordenar los principales candidatos según oom_score
for p in /proc/[0-9]*; do
  pid=${p##*/}
  [ -r "$p/oom_score" ] || continue
  printf "%6s  %5s  %-30s\n" \
    "$(cat $p/oom_score)" \
    "$(cat $p/oom_score_adj 2>/dev/null || echo 0)" \
    "$(tr -d '\0' < $p/comm)"
done | sort -nr | head -n 20

Si veo OOM repetidamente, lo documento Línea de base Estos valores en condiciones normales de funcionamiento y compáralos con los de la ventana del incidente. Las desviaciones saltan a la vista de inmediato, como un aumento incontrolado del PSS o «slabs» desproporcionadamente grandes.

Systemd, contenedores y orquestación: control específico

En systemd configuro las prioridades y los límites declarado en archivos Unit:

[Servicio]
# Proteger el proceso o hacer que sea sacrificable
OOMScoreAdjust=-900

# Límites de memoria rígidos/flexibles (cgroup v2)
MemoryMax=8G
MemoryHigh=6G
# Opcional: limitar el espacio de intercambio
MemorySwapMax=2G

# Comportamiento ante OOM en systemd
# (p. ej., forzar el reinicio)
Restart=on-failure
RestartSec=5

En entornos de contenedores, me encargo de establecer límites claros para cada servicio. Para mí es importante distinguir entre Solicitar (reserva prevista) y Límite (límite máximo estricto). Los pods con solicitudes y límites adecuados obtienen mejores clasificaciones de calidad de servicio (QoS); las cargas de trabajo „BestEffort“ corren el riesgo de sufrir OOM. Un detalle práctico: si el núcleo cierra un contenedor debido a un OOM de Cgroup, suelo ver el código de salida 137 y acontecimientos con OOMKilled; en el servidor‑dmesg Esto se puede correlacionar. En los clústeres productivos, planifico las implementaciones críticas como „Guaranteed“, mientras que los trabajos por lotes se ejecutan deliberadamente con menos frecuencia y, por lo tanto, ceden el paso en primer lugar.

Detalles del núcleo: OOM Reaper, THP y fragmentación

Tras la muerte, el OOM Reaper: un hilo del núcleo retira lo antes posible el mapeo de memoria del proceso víctima, para que la RAM quede realmente libre. Esto explica por qué, a veces, la memoria no se libera hasta a se vuelve a ver la entrada de Kille. Paralelamente, la Compactación de la memoria alcanzar sus límites: si la RAM está muy fragmentada, faltan áreas contiguas para asignaciones de gran tamaño (por ejemplo, con Transparent Huge Pages, THP). Las THP ofrecen rendimiento, pero pueden dificultar las asignaciones cuando se encuentran bajo presión. Para cargas de trabajo en las que la latencia es crítica, desactivo o limito las THP a modo de prueba y mido los efectos.

Otro factor es Cachés de slab y la caché de páginas: en cargas de trabajo con gran volumen de E/S, estas cachés crecen considerablemente. Con vm.vfs_cache_pressure y, mediante una recuperación selectiva, se puede controlar su volumen; el vaciado general (Drop-Caches) lo utilizo, como mucho, como herramienta de diagnóstico, no como solución permanente. Además, presto atención a NUMA: Si se agota la memoria de un nodo, un proceso de esa zona NUMA puede fallar a pesar de que haya memoria RAM libre a nivel global. También aparecen mensajes al respecto en los registros del núcleo.

Profundizar en las estrategias de swap: «swappiness», ZRAM/Zswap, presupuesto de E/S

El swap no es algo malo, sino un Amortiguador. Lo importante es utilizarlo con inteligencia. Con vm.swappiness ajusto el momento en que el núcleo recurre al espacio de intercambio. Unos valores demasiado bajos hacen que la caché de páginas domine y pueden provocar errores de memoria insuficiente (OOM) antes de lo previsto; unos valores demasiado altos desplazan la carga hacia las E/S del espacio de intercambio y ralentizan el sistema. En hosts compactos, suelo utilizar ZRAM o Zswap, con el fin de crear un búfer comprimido que absorba los picos sin sobrecargar el disco. Es importante recordar que el swap no sustituye a la capacidad que falta. Solo gana tiempo para que el OOM-Killer no tenga que activarse.

Casos especiales: mlock, RLIMITS, problemas con el overcommit

Algunas condiciones marco aumentan los riesgos de OOM o modifican su comportamiento:

  • Memoria bloqueada: Procesos que se realizan a través de mlock() Al fijar páginas, se las retira del Reclaim. Si la tasa es elevada, se puede ralentizar la velocidad del Reaper.
  • RLimits: RLIMIT_AS y RLIMIT_RSS Establecen límites máximos por proceso y evitan que los servicios individuales se desborden, lo que constituye una medida para prevenir los errores OOM.
  • Overcommit: Unos ajustes de overcommit demasiado generosos permiten un espacio de direcciones virtual muy amplio que luego no se puede cubrir físicamente. Precisamente los picos de asignación de muchos subprocesos provocan entonces errores y aceleran los eventos OOM.
  • panic_on_oom: En el caso de los sistemas de alta criticidad, existe la opción de responder a un error OOM con un «kernel panic». Esto solo tiene sentido en escenarios de alta disponibilidad (HA) muy concretos; en los demás casos resulta contraproducente.
  • „Unkillbar“ es arriesgado: oom_score_adj=-1000 Aunque protege contra el «killer», puede bloquear todo el sistema. Yo solo lo utilizo para procesos pequeños y absolutamente esenciales (por ejemplo, «init»), no para servicios de servidor que consumen mucha memoria.

En la práctica: establecer prioridades y garantizar los cambios

En el equipo defino una Clasificación Los servicios: ¿qué debe quedarse y qué puede eliminarse primero? Este orden lo plasmo en oom_score_adj, límites de Cgroup y (cuando estén disponibles) políticas de reinicio. Los cambios se incorporan como código en los manifiestos de unidad o de implementación, acompañados de puntos de medición en la supervisión. En las pruebas de carga simulo la presión sobre la memoria: aumento el volumen de datos, elevo el nivel de paralelismo, dejo que las cachés crezcan… y observo si caen precisamente los procesos „sacrificables“, mientras que los componentes centrales permanecen en línea. Solo cuando esto funciona de forma reproducible, la configuración pasa a producción.

Patrones de diagnóstico: distinguir entre fugas, montones y fragmentación

No todo aumento del RSS supone una fuga. Yo distingo sistemáticamente entre:

  • Fuga: Los valores RSS/PSS aumentan de forma monótona, incluso sin un aumento de la carga; smaps_rollup crece de forma uniforme; los ciclos GC (en entornos de ejecución gestionados) no sirven de nada.
  • Picos de la pila: El RSS aumenta con la carga y vuelve a descender; la caché de páginas se correlaciona con los patrones de E/S.
  • Fragmentación: Hay suficiente memoria RAM libre, pero las asignaciones de gran tamaño fallan; los registros muestran intentos de compactación, y las asignaciones de THP fallan con mayor frecuencia.

Para las cargas de trabajo de JVM o Node, compruebo si el entorno de ejecución detecta los límites de los contenedores. Unos montones (heaps) o cachés de código JIT demasiado grandes pueden sobrepasar los límites y provocar errores de memoria insuficiente (OOM), aunque aparentemente aún quede margen. Configuro los montones, incluida la sobrecarga, de tal forma que, en MemoryMax aún queda espacio libre para las partes nativas, las pilas de subprocesos y la caché de páginas.

Guía práctica para implementaciones y pruebas de carga en condiciones de presión de almacenamiento

  1. Medir el valor de referencia: RSS/PSS por servicio, proporciones de Slab, cuota de swap, tamaños de caché, oom_score.
  2. Establecer límites: Memoria máxima/máxima o definir límites de contenedores con un margen realista; OOMScoreAdjust asignados por orden de prioridad.
  3. Generar estrés: Volumen de datos, concurrencia, crecimiento de la caché; anotar los perfiles de E/S y CPU.
  4. Observe: dmesg -T, métricas de host y de cgroup; comprueba quién es el primero en verse sometido a presión.
  5. Iterar: Ajustar los límites y los valores de ajuste, ajustar el «swappiness», comprobar la configuración de THP y volver a realizar la medición.
  6. Automatice: Integrar comprobaciones en CI/CD, alertas cuando se superan los valores límite, políticas de reinicio para los servicios afectados.

En resumen: medidas concretas

Entiendo que el OOM Killer es Señal, que mi sistema tenía antes muy poco margen o que los procesos no estaban priorizados correctamente. Con una supervisión adecuada, límites realistas, una estrategia de intercambio bien definida y un uso consciente de oom_score_adj Reduzco considerablemente los cierres bruscos. En entornos productivos, protejo los procesos críticos, hago que los servicios secundarios sean prescindibles y mido cada cambio. En el caso de los contenedores, establezco límites de Cgroup de forma estricta para que un servicio no bloquee todo el sistema host. Quien mantenga esta disciplina conseguirá que Linux siga siendo receptivo incluso bajo presión y reducirá considerablemente el tiempo necesario para identificar la causa.

Artículos de actualidad

Rack de servidores con sistemas Linux y visualización del uso de la memoria
Servidores y máquinas virtuales

Entender el OOM Killer: cuando Linux termina procesos

Descubre cómo funciona el OOM Killer en Linux cuando hay falta de memoria, cómo termina los procesos y cómo, como administrador en entornos de alojamiento web, puedes evitar los problemas de falta de memoria utilizando la palabra clave «oom killer linux».

Un administrador analiza los registros de «journalctl» en un servidor Linux del centro de datos
Administración

Uso eficaz de `journalctl`: análisis de errores en servidores Linux

Aprende a utilizar `journalctl` para analizar de forma eficaz los errores en servidores Linux. Con filtros de fecha y hora, servicio y prioridad, podrás analizar los registros de Linux de forma estructurada y optimizar la resolución de problemas en tus servidores.