...

Capire l’OOM Killer: quando Linux termina i processi

L'articolo spiega come il oom killer linux interviene in caso di gravi carenze di memoria e perché termina forzatamente i servizi per mantenere il server operativo. Vi mostrerò passo dopo passo come individuare i fattori scatenanti, comprendere il sistema di punteggio e gestire il comportamento in condizioni di pressione sulla memoria tramite impostazioni mirate.

Punti centrali

  • Innesco: Gli eventi OOM si verificano quando la RAM è piena, lo spazio di swap è esaurito e i tentativi di recupero non vanno a buon fine.
  • Punteggio: Il kernel assegna un oom_score e termina i processi che occupano una grande quantità di memoria.
  • Riconoscimento: Per ulteriori informazioni, consultare dmesg con „Out of memory“ e „Killed process“.
  • Sistema di controlloCon oom_score_adj assegno la priorità ai servizi in base alla loro importanza.
  • Prevenzione: Il monitoraggio, i limiti, la strategia di swap e l'analisi delle perdite evitano le uccisioni definitive.

Che cos’è l’OOM Killer nel kernel di Linux?

L'OOM Killer è l'ultimo Linea di sicurezza del kernel e termina i processi quando non sono più disponibili pagine di memoria. Lo considero una sorta di „arresto di emergenza“ controllato, che impedisce un’interruzione totale del sistema e libera immediatamente spazio nella RAM. In precedenza, il sistema cerca di trasferire le pagine su disco o di svuotare le cache, ma se la pressione persiste non resta che ricorrere a un taglio netto. Nei log riconosco questo momento grazie a voci con „Out of memory“ e «Killed process», spesso accompagnate da un SIGKILL. Questa funzionalità è attiva per impostazione predefinita e viene eseguita automaticamente, il che spesso causa sorprese negli ambienti di produzione quando servizi importanti scompaiono senza preavviso.

Quando entra in funzione il meccanismo?

Gli eventi OOM si verificano quando la RAM fisica è quasi completamente occupata, lo swap non è più in grado di fornire spazio di buffer e le richieste di nuove pagine falliscono. In questa situazione, il kernel valuta se sia possibile recuperare spazio dalla Cache della pagina e lo swapping è ancora utile, e il "killer" viene avviato solo quando la situazione è senza speranza. I picchi di breve durata non attivano direttamente il meccanismo; occorrono una pressione prolungata e tentativi vani di liberazione. Per l’analisi mi è utile dare un’occhiata all’RSS, all’utilizzo dello swap, alla percentuale di cache di pagina e alle allocazioni per gruppo di controllo. Chi desidera comprendere più a fondo come funziona lo svuotamento della cache può consultare le informazioni di base su Eviction della cache delle pagine visualizzarli e misurarne gli effetti sul proprio sistema.

In che modo il kernel seleziona la „vittima“?

La selezione segue un Punteggio, che Linux come oom_score calcolato per ogni processo. Una quota elevata della memoria totale, un valore RSS elevato e un ruolo non critico determinano un punteggio più alto. I processi legati al sistema, come init, ricevono una penalizzazione, mentre i processi worker o le cache che consumano molta memoria sono spesso in testa. Tramite oom_score_adj posso modificare il punteggio in modo mirato e quindi controllare la catena delle vittime. In genere, il kernel termina il processo con il punteggio più alto tramite SIGKILL, per liberare il più possibile la RAM in un colpo solo.

Riconoscere le tracce: log e segnali

Dopo aver terminato improvvisamente il turno, per prima cosa controllo dmesg e i log del kernel. Se lì compaiono messaggi come „Out of memory“ e „Killed process“, annoto il PID, il nome del processo, l’utente e il punteggio calcolato. Spesso nell’applicazione stessa manca un log degli errori, perché SIGKILL non consente una fase di pulizia. Confronto i tempi con i dati di monitoraggio per ricostruire l’aumento di RAM, swap e RSS per ogni processo. In questo modo identifico in modo affidabile e rapido le perdite di memoria, gli heap troppo grandi o i limiti mancanti.

Assumere il controllo con oom_score e oom_score_adj

Ogni processo possiede in /proc/[PID]/oom_score un aggiornato Valore per la sua situazione di rischio. Con /proc/[PID]/oom_score_adj Riduco o aumento la probabilità che il kernel termini quel processo. Proteggo i servizi critici, come i database, con un valore Adj negativo, mentre rendo i worker non essenziali „sacrificabili“ con un valore Adj positivo. La modifica ha effetto immediato, il che è particolarmente utile durante i rollout o i test di carico. In questo modo trasformo un meccanismo di emergenza imprevedibile in uno strumento che segue le mie priorità.

Scenari tipici di hosting con limitazioni di spazio di archiviazione

In ambienti con molti container, database e cache, mi capita di vedere l’OOM Killer particolarmente spesso. Un database che cresce in modo incontrollato spinge fuori altri servizi dal RAM e porta a crash gravi. Le perdite di memoria nelle applicazioni web accumulano pressione nel corso delle ore, fino a quando nemmeno il reclaim è più d’aiuto. Limiti dei container troppo generosi su un host troppo piccolo aggravano ulteriormente la situazione. Chi conosce questi schemi imposta allarmi tempestivi e interviene prima che il “killer” prenda il sopravvento.

Migliori pratiche per evitare le uccisioni violente

Pianifico lo spazio di memoria in modo realistico, prevedo un margine per i picchi di carico e stabilisco limiti chiari per ogni servizio. Il monitoraggio rileva, per ogni processo, l'RSS, l'utilizzo dello swap e oom_score, in modo che gli avvisi vengano attivati prima che si verifichi un'emergenza. Nelle configurazioni containerizzate imposto dei limiti ai cgroup, in modo che i singoli servizi non sovraccarichino l’host. Una strategia di swap ben congegnata gestisce i picchi di carico senza rallentare il sistema in modo permanente. Per una comprensione più approfondita e una pianificazione più accurata, mi avvalgo di guide pratiche su Gestire la memoria virtuale, in modo che i carichi di lavoro dispongano di spazio sufficiente.

Procedura strutturata in caso di evento OOM

Dopo l'incidente, per prima cosa recupero i log dmesg e kern.log e li ordino cronologicamente. Nel monitoraggio controllo le curve relative a RAM, swap, RSS e page-cache per osservare l’andamento del carico. Successivamente verifico gli ulimits, i limiti dei Cgroup e dei container, nonché i parametri delle applicazioni come gli heap delle JVM. Infine regolo oom_score_adj in modo che rimangano le cose più importanti e quelle superflue vengano eliminate per prime. Infine, risolvo la causa: elimino le perdite, limito le cache, riduco il parallelismo e dimensiono correttamente le capacità.

Caratteristiche specifiche degli ambienti VPS e cloud

Sulle macchine virtuali si aggiunge un secondo livello di limitazioni, ad esempio dovuto all’hypervisor o all’orchestrazione. Conosco quindi la memoria assegnata RAM-Imposta con precisione la quantità e configura i limiti di Kubernetes o dei container in modo adeguato. L’OOM Killer di Linux continua a funzionare come descritto, ma i meccanismi del provider possono innescare ulteriori limitazioni. Soprattutto in presenza di molti pod, è utile stabilire una chiara gerarchia di priorità: ai deployment importanti vengono assegnate risorse di riserva, mentre i job non critici vengono eseguiti con maggiori restrizioni. La documentazione fornita dal provider della piattaforma e i test interni consentono di evitare sorprese durante il funzionamento in produzione.

Ottimizzazione della memoria: overcommit, swappiness e cache

Chi vuole gestire i rischi legati all'OOM deve regolare con attenzione i parametri del kernel e comprenderne le interazioni. vm.overcommit_memory e vm.overcommit_ratio controllare quanto Linux consenta di essere generoso con le allocazioni virtuali, mentre vm.swappiness influisce sul rapporto tra swapping e reclaim. vm.vfs_cache_pressure regola il livello di aggressività nella liberazione delle cache di inode e dentry, influendo così direttamente sullo spazio disponibile nella cache di pagina. Verifico sempre gli effetti in condizioni realistiche Carico, registra le metriche e apporta modifiche solo in modo graduale. Per ulteriori informazioni sul contesto e sugli scenari, è utile dare un’occhiata a Overcommit della memoria, per scegliere in modo oculato le proprie impostazioni predefinite.

Parametro/Indicatore Ruolo nel sistema Dove effettuare il controllo Direzione abituale
RAM gratuita Un ammortizzatore contro le uccisioni brutali free, /proc/meminfo Mantenere una riserva sufficiente
Utilizzo dello swap Ammortizzatori per picchi free, vmstat Da basso a moderato
vm.overcommit_memory Assegnazione virtuale sysctl 0/2 a seconda del rischio
vm.overcommit_ratio Quota per l'overcommit sysctl Adatto al carico di lavoro
vm.swappiness Propensione allo swap sysctl Il valore medio anziché quello estremo
vm.vfs_cache_pressure Recupero delle cache VFS sysctl 100 come punto di partenza

OOM globale vs. OOM del cgroup: cosa viene terminato esattamente?

Nelle configurazioni moderne con Cgroups (v1/v2), un evento OOM può locale in un Memory-cgroup oppure globale venga avviato sull'host. Se un processo viene eseguito in un container con memoria.max (ovvero limite), il kernel in genere termina solo i processi presenti in quel Cgroup („memcg OOM“), mentre il sistema nel suo complesso continua a funzionare. In dmesg Lo riconosco da indizi quali constraint=CONSTRAINT_MEMCG o riferimenti al Cgroup interessato. Solo quando nessun Cgroup è più in grado di cedere RAM e la memoria globale è esaurita, interviene il a livello di sistema OOM Killer. Per garantire la stabilità, ritengo importante impostare i limiti in modo tale che un servizio che supera i limiti fallisca all’interno del proprio Cgroup, anziché compromettere l’intero host. Nei Cgroup v2 posso inoltre utilizzare memoria.alta impostare limitazioni di potenza moderate e con memory.oom.group stabilire che, in caso di emergenza, l'intero gruppo venga terminato – una soluzione più pulita rispetto a un processo residuo che continua a funzionare a metà.

Strumenti e metriche nella pratica

Per individuare rapidamente le cause, raccolgo dati riproducibili. Questi strumenti mi sono regolarmente d’aiuto:

  • Panoramica del processo: ps -eo pid,ppid,cmd,%mem,rss --sort=-rss | head indica le applicazioni che consumano molta memoria.
  • Smaps‑Rollup: cat /proc//smaps_rollup Fornisce i valori RSS/PSS/Swap di un processo senza richiedere un’analisi approfondita.
  • pmap: pmap -x | sort -nrk3 | head Elenca i mapping con dimensioni e RSS, ideali per gli heap e i segmenti di grandi dimensioni.
  • Utilizzo delle lastre: slabtop -o mostra le cache del kernel, che possono aumentare di dimensioni in caso di carico elevato.
  • Pressione di sistema: vmstat 1 e sar -r 1 forniscono informazioni sul paging, sullo swap I/O e sulle operazioni di liberazione della memoria.
  • Statistiche dei cgroup: Nella versione 2 controllo /sys/fs/cgroup/memory.current, memory.swap.current e memory.stat del servizio in questione.
Leggere comodamente i log OOM di #
dmesg -T | egrep -i 'out of memory|oom-kill|killed process'

# Ordinare i candidati principali in base a 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

Se vedo ripetutamente degli OOM, ne prendo nota Linea di base questi valori in condizioni di funzionamento normale e confrontali con la finestra relativa all'evento. Le anomalie saltano subito all'occhio, come ad esempio un aumento incontrollato del PSS o slab di dimensioni sproporzionate.

Systemd, container e orchestrazione: un controllo mirato

Con systemd imposto le priorità e i limiti dichiarato nei file Unit:

[Servizio]
# Proteggere il processo o renderlo sacrificabile
OOMScoreAdjust=-900

# Limiti di memoria rigidi/morbidi (cgroup v2)
MemoryMax=8G
MemoryHigh=6G
# Opzionale: limitare lo swap
MemorySwapMax=2G

# Comportamento in caso di OOM con systemd
# (ad es. forzare il riavvio)
Restart=on-failure
RestartSec=5

Negli ambienti containerizzati mi assicuro che vi siano limiti chiari per ogni servizio. Per me è importante distinguere tra Richiesta (prenotazione prevista) e Limite (limite massimo rigido). I pod con richieste/limiti adeguati ottengono classificazioni QoS migliori; i carichi di lavoro „BestEffort“ sono a rischio di OOM. Un dettaglio pratico: se il kernel termina un container a causa di un Cgroup OOM, vedo spesso il codice di uscita 137 e gli eventi con OOMKilled; nell'host‑dmesg È possibile stabilire una correlazione. Nei cluster produttivi pianifico le distribuzioni critiche come „Guaranteed“, mentre i processi batch vengono eseguiti intenzionalmente con tempi più stretti e quindi cedono il passo per primi.

Dettagli sul kernel: OOM Reaper, THP e frammentazione

Dopo aver ucciso il bersaglio, il OOM Reaper: un thread del kernel revoca il mapping di memoria al processo vittima il più rapidamente possibile, in modo che la RAM venga effettivamente liberata. Questo spiega perché a volte la memoria viene liberata solo a riappare visibilmente nella voce "Kille". Parallelmente, la Compattazione della memoria raggiungere i propri limiti: se la RAM è fortemente frammentata, mancano aree contigue per allocazioni di grandi dimensioni (ad esempio con le Transparent Huge Pages, THP). Le THP garantiscono prestazioni elevate, ma in condizioni di carico elevato possono complicare le operazioni di allocazione. Per i carichi di lavoro in cui la latenza è un fattore critico, disattivo o limito le THP a titolo sperimentale e ne misuro gli effetti.

Un altro fattore è rappresentato da Cache su piastra e la cache di pagina: in presenza di carichi di lavoro con elevato carico di I/O, queste cache crescono notevolmente. Con vm.vfs_cache_pressure e grazie a un recupero mirato è possibile controllarne la percentuale; lo svuotamento forfettario (drop-cache) lo utilizzo al massimo come strumento diagnostico, non come soluzione a lungo termine. Inoltre, faccio attenzione a NUMA: Se un nodo di memoria è esaurito, un processo in quella zona NUMA può fallire nonostante la RAM globale sia libera. Relativi messaggi compaiono anche nei log del kernel.

Approfondimento sulle strategie di swap: Swappiness, ZRAM/Zswap, budget I/O

Lo swap non è un male, ma un Ammortizzatori. L'importante è utilizzarlo con intelligenza. Con vm.swappiness regolo la rapidità con cui il kernel ricorre allo swap. Valori troppo bassi fanno sì che la cache di pagina diventi predominante e possono innescare prima gli OOM; valori troppo alti spostano il carico verso gli I/O dello swap e rendono il sistema lento. Negli host compatti mi piace utilizzare ZRAM oppure Zswap, al fine di creare un buffer compresso che assorba i picchi senza sovraccaricare il disco. È importante ricordare che lo swap non sostituisce la capacità mancante. Serve solo a guadagnare tempo, in modo che l’OOM-killer non debba nemmeno attivarsi.

Casi particolari: mlock, RLIMITS, insidie dell'overcommit

Alcune condizioni al contorno aumentano i rischi di OOM o ne modificano il comportamento:

  • Memoria bloccata: Processi che avvengono tramite mlock() Fissando le pagine, le si sottraggono al Reclaim. Se la frequenza è elevata, è possibile rallentare la velocità del Reaper.
  • RLimits: RLIMIT_AS e RLIMIT_RSS stabiliscono limiti massimi per ogni processo e impediscono che i singoli servizi si espandano eccessivamente: un elemento fondamentale per prevenire gli errori OOM.
  • Impegno eccessivo: Impostazioni di overcommit troppo generose consentono di creare uno spazio di indirizzamento virtuale di grandi dimensioni, che in seguito non potrà essere coperto fisicamente. Proprio i picchi di allocazione causati dalla presenza di molti thread provocano contemporaneamente errori di accesso e accelerano il verificarsi di eventi OOM.
  • panic_on_oom: Per i sistemi altamente critici è prevista la possibilità di reagire a un errore OOM con un kernel panic. Ciò ha senso solo in scenari di alta disponibilità (HA) ben definiti, mentre in altri casi risulta controproducente.
  • „Unkillbar“ è rischioso: oom_score_adj=-1000 Sebbene protegga dal "killer", può bloccare l'intero sistema. Lo utilizzo solo per processi piccoli e assolutamente essenziali (ad es. init), non per servizi server che richiedono molta memoria.

Pratica: definire le priorità e garantire i cambiamenti

All’interno del team definisco una Classifica dei servizi: cosa deve rimanere, cosa può essere eliminato per primo? Questo ordine lo traduco in oom_score_adj, limiti dei cgroup e (ove disponibili) politiche di riavvio. Le modifiche vengono integrate nel codice dei manifesti delle unità o di distribuzione, accompagnate da punti di monitoraggio. Nei test di carico simulo la pressione sulla memoria: aumento i volumi di dati, innalzo il livello di parallelismo, lascio crescere le cache – e osservo se cadono proprio i processi „sacrificabili“, mentre i componenti fondamentali rimangono online. Solo quando questo funziona in modo riproducibile, la configurazione viene implementata in produzione.

Modelli diagnostici: distinguere tra perdite, heap e frammentazione

Non tutti gli aumenti dell'RSS sono una perdita. Faccio sistematicamente una distinzione:

  • Fuga: RSS/PSS aumentano in modo monotono, anche in assenza di un aumento del carico; smaps_rollup cresce in modo uniforme; i cicli GC (nei runtime gestiti) non sono d’aiuto.
  • Picchi dell'heap: L'RSS aumenta con il carico e poi diminuisce nuovamente; la cache delle pagine è correlata ai modelli di I/O.
  • Frammentazione: RAM libera sufficiente, ma le allocazioni di grandi dimensioni falliscono; i log mostrano tentativi di compattazione, le assegnazioni THP falliscono più spesso.

Per i carichi di lavoro JVM o Node, verifico se il runtime riconosce i limiti dei container. Heap o cache di codice JIT sovradimensionati possono superare i limiti previsti e provocare errori OOM, anche se apparentemente c’è ancora margine. Impostiamo gli heap, compreso l’overhead, in modo tale che sotto MemoryMax rimane ancora spazio per le quote native, gli stack dei thread e la cache di pagina.

Guida operativa per l'implementazione e i test di carico in condizioni di pressione di memorizzazione

  1. Misurare il valore di riferimento: RSS/PSS per servizio, percentuali Slab, quota di swap, dimensioni della cache, oom_score.
  2. Stabilire dei limiti: Memoria massima/massima oppure definire limiti per i container con un margine realistico; OOMScoreAdjust assegnati in base alla priorità.
  3. Generare stress: volume dei dati, concorrenza, crescita della cache; prendere nota dei profili I/O e CPU.
  4. Osservare: dmesg -T, metriche relative all’host e ai cgroup; verificare chi va in crisi per primo.
  5. Iterare: Regolare i limiti/Adj, modificare lo swappiness, verificare le impostazioni THP, ripetere la misurazione.
  6. Automatizzare: Integrare i controlli nel CI/CD, avvisi in caso di superamento dei valori limite, politiche di riavvio per i servizi compromessi.

In breve: misure concrete

Per me l'OOM Killer è Segnale, che in precedenza il mio sistema avesse una memoria di buffer insufficiente o che i processi fossero stati assegnati in modo errato in termini di priorità. Grazie al monitoraggio, a limiti realistici, a una strategia di swap ben definita e a un uso consapevole di oom_score_adj Riduco notevolmente i crash improvvisi. Nelle configurazioni produttive proteggo i processi fondamentali, rendo superflui i servizi secondari e monitoro ogni modifica. Per quanto riguarda i container, definisco rigorosamente i limiti dei Cgroup, in modo che un servizio non blocchi l’intero sistema host. Chi mantiene questa disciplina garantisce la reattività di Linux anche sotto pressione e riduce notevolmente il tempo necessario per individuare la causa del problema.

Articoli attuali

Rack di server con sistemi Linux e utilizzo della memoria visualizzato
Server e macchine virtuali

Capire l’OOM Killer: quando Linux termina i processi

Scopri come funziona l’OOM Killer in Linux in caso di carenza di memoria, come termina i processi e come, in qualità di amministratore in ambienti di hosting, puoi evitare i problemi di esaurimento della memoria utilizzando la parola chiave “oom killer linux”.

L'amministratore analizza i log di Journalctl sui server Linux nel centro dati
Amministrazione

Utilizzo efficace di `journalctl`: analisi degli errori sui server Linux

Scopri come utilizzare `journalctl` per un'analisi efficiente degli errori sui server Linux. Grazie ai filtri per data, servizio e priorità, potrai analizzare i log di Linux in modo strutturato e ottimizzare la risoluzione dei problemi sui tuoi server.