...

Att förstå OOM Killer: När Linux avslutar processer

Artikeln förklarar hur oom killer linux griper in vid akuta minnesbrist och varför den avslutar tjänster med våld för att hålla servern i drift. Jag visar steg för steg hur jag identifierar utlösande faktorer, förstår poängsättningen och styr beteendet vid minnesbrist med hjälp av riktade inställningar.

Centrala punkter

  • Avtryckare: OOM-händelser inträffar när RAM-minnet är fullt, swap-utrymmet är uttömt och återvinningsförsöken misslyckas.
  • Poängräkning: Kärnan tilldelar en oom_score och avslutar processer som tar upp mycket minne.
  • Erkännande: Anvisningar finns i dmesg med „Out of memory“ och „Killed process“.
  • StyrsystemMed oom_score_adj Jag prioriterar tjänsterna utifrån deras betydelse.
  • Förebyggande åtgärder: Övervakning, gränsvärden, swap-strategi och läckageanalys förhindrar plötsliga avbrott.

Vad är OOM Killer i Linux-kärnan?

OOM Killer är den sista Säkerhetslinje i kärnan och avslutar processer när det inte finns några minnessidor kvar. Jag ser det som en kontrollerad nödstopp som förhindrar ett totalt haveri och omedelbart frigör RAM-minne. Innan dess försöker systemet att byta ut sidor eller tömma cacheminnen, men vid ihållande belastning återstår endast den hårda avbrytningen. I loggarna känner jag igen detta ögonblick på poster med „Out of memory“ och „Killed process“, ofta åtföljda av ett SIGKILL. Funktionen är aktiverad som standard och körs automatiskt, vilket ofta leder till överraskningar i produktiva miljöer när viktiga tjänster försvinner utan förvarning.

När träder mekanismen i kraft?

OOM-händelser inträffar när det fysiska RAM-minnet är nästan fullt, swap-utrymmet inte längre kan fungera som buffert och förfrågningar om nya sidor misslyckas. I denna situation bedömer kärnan om återvinning från Sidans cache och att swapping fortfarande hjälper, och startar först ”killer”-mekanismen när situationen är hopplös. Kortsiktiga toppar utlöser inte mekanismen direkt; det krävs ihållande belastning och misslyckade försök att frigöra minne. För analysen hjälper det mig att titta på RSS, swap-användning, andel sidcache och tilldelningarna per kontrollgrupp. Den som vill förstå djupare hur cache-förträngning fungerar kan läsa mer om bakgrunden till Rensning av sidcache granska och mäta effekterna i det egna systemet.

Hur väljer kärnan ut „offret“?

Urvalet följer en Poängräkning, där Linux fungerar som oom_score beräknas per process. En stor andel av det totala minnet, mycket RSS och en icke-kritisk roll leder till en högre poäng. Systemnära processer som init får en poängavdrag, medan minneskrävande arbetsprocesser eller cacher ofta hamnar i topp. Över oom_score_adj kan jag justera poängen på ett målinriktat sätt och därmed styra vilka processer som ska avslutas. Vanligtvis avslutar kärnan den process som har högst poäng med SIGKILL för att frigöra så mycket RAM-minne som möjligt på en gång.

Att upptäcka spår: loggar och signaler

Efter att ha avslutat mitt arbete plötsligt kontrollerar jag först dmesg och kärnloggarna. Om „Out of memory“ och „Killed process“ dyker upp där noterar jag PID, processnamn, användare och den beräknade poängen. Ofta saknas en fellogg i själva applikationen, eftersom SIGKILL inte tillåter någon upprensningsfas. Jag jämför tidpunkten med övervakningsdata för att följa ökningen av RAM, swap och RSS per process. På så sätt identifierar jag läckor, för stora heap eller saknade gränsvärden på ett tillförlitligt och snabbt sätt.

Få kontroll med oom_score och oom_score_adj

Varje process har i /proc/[PID]/oom_score en aktuell Värde med tanke på hans utsatta situation. Med /proc/[PID]/oom_score_adj Minskar eller ökar jag sannolikheten för att kärnan avslutar den här processen. Kritiska tjänster som databaser skyddar jag med ett negativt Adj, medan jag gör oviktiga arbetare „offerbara“ med ett positivt Adj. Ändringen träder i kraft omedelbart, vilket är särskilt användbart vid utrullningar eller belastningstester. På så sätt omvandlar jag en oförutsägbar nödmekanism till ett verktyg som följer mina prioriteringar.

Typiska webbhotellsscenarier med begränsat lagringsutrymme

I miljöer med många containrar, databaser och cacher stöter jag särskilt ofta på OOM-killer. En databas som växer okontrollerat tränger undan andra tjänster från RAM och leder till allvarliga krascher. Minnesläckor i webbapplikationer bygger upp tryck under flera timmar, tills inte ens minnesåtervinning hjälper längre. Alltför generösa containergränser på en för liten värd förvärrar situationen ytterligare. Den som känner till dessa mönster sätter upp larm i god tid och ingriper innan kraschen tar över.

Bästa praxis för att undvika ”hard kills”

Jag planerar lagringsutrymmet på ett realistiskt sätt, lägger in buffert för toppbelastning och sätter tydliga gränser per tjänst. Övervakningen registrerar RSS, swap-användning och oom_score, så att varningar utlöses innan en allvarlig situation uppstår. I container-miljöer sätter jag Cgroup-gränser så att enskilda tjänster inte dominerar värdmaskinen. En väl genomtänkt swap-strategi hanterar toppbelastningar utan att systemet blir permanent långsammare. För att få en djupare förståelse och kunna planera bättre använder jag mig av praktiska handledningar om hantera virtuellt minne, så att arbetsbelastningarna får tillräckligt med utrymme.

Strukturerad hantering vid ett OOM-fel

Efter händelsen säkerhetskopierar jag först loggfilerna dmesg och kern.log och ordnar dem kronologiskt. I övervakningen granskar jag kurvorna för RAM, swap, RSS och sidcache för att se belastningsförloppet. Därefter kontrollerar jag ulimits, Cgroup- och containergränser samt applikationsparametrar som JVM:ernas heap. Därefter justerar jag oom_score_adj så att det viktigaste får stå kvar och det överflödiga tas bort först. Till sist åtgärdar jag orsaken: åtgärdar läckor, begränsar cacheminnen, minskar parallelliteten och dimensionerar kapaciteten korrekt.

Särdrag i VPS- och molnmiljöer

På virtuella maskiner tillkommer en andra nivå av begränsningar, till exempel genom hypervisorn eller orkestreringen. Jag känner därför till den tilldelade RAM-Mängden exakt och ställ in Kubernetes- eller containergränser på lämpligt sätt. Linux OOM Killer fungerar fortfarande som beskrivits, men leverantörsmekanismer kan utlösa ytterligare begränsningar. Särskilt vid många poddar är det bra med en tydlig prioritering: viktiga distributioner tilldelas reserver, medan icke-kritiska jobb körs med mindre resurser. Dokumentation från plattformsleverantören och egna tester förhindrar överraskningar i produktiv drift.

Finjustering av minnet: Overcommit, Swappiness och cacher

Den som vill hantera OOM-risker justerar kärnparametrarna med omtanke och förstår hur de påverkar varandra. vm.overcommit_memory och vm.overcommit_ratio styra hur generösa virtuella tilldelningar Linux tillåter, medan vm.swappiness påverkar förhållandet mellan swapping och reclaim. vm.vfs_cache_tryck styr aggressiviteten vid frigörande av inode- och dentry-cacher och påverkar därmed direkt utrymmet i sidcachen. Jag testar alltid effekterna under realistiska Last, logga mätvärdena och gör endast stegvisa ändringar. För bakgrundsinformation och scenarier kan det vara bra att ta en titt på Överbeläggning av minne, för att välja lämpliga standardinställningar.

Parameter/nyckeltal Roll i systemet Var ska man kontrollera Vanlig riktning
Gratis RAM-minne Buffert mot hårda dödsslag free, /proc/meminfo Se till att ha tillräckligt med reserver
Användning av swap Stötdämpare för toppar free, vmstat Låg till måttlig
vm.overcommit_memory Virtuell tilldelning sysctl 0/2 beroende på risk
vm.overcommit_ratio Kvot för överbeläggning sysctl Anpassat efter arbetsbelastningen
vm.swappiness Benägenhet att byta sysctl Medelvärde istället för extremvärde
vm.vfs_cache_tryck Återvinning av VFS-cacher sysctl 100 som utgångspunkt

Global OOM kontra Cgroup-OOM: vad är det egentligen som avslutas?

I moderna konfigurationer med Cgroups (v1/v2) kan en OOM-händelse lokal i en Memory-cgroup eller Globalt utlösas på värddatorn. Om en process körs i en container med strikt minne.max (eller gräns) avslutar kärnan vanligtvis endast processer i denna C-grupp („memcg OOM“), medan resten av systemet fortsätter att köras. I dmesg Det märker jag på tecken som constraint=CONSTRAINT_MEMCG eller hänvisningar till den berörda C-gruppen. Först när ingen C-grupp längre kan avsätta RAM-minne och det globala minnet är uttömt, träder systemomfattande OOM Killer. För att säkerställa stabiliteten är det viktigt för mig att ställa in gränser så att en tjänst som överskrider gränserna kraschar i sin egen Cgroup, istället för att dra med sig hela värddatorn. I Cgroups v2 kan jag dessutom med minne.hög ställa in mjuka strypningar och med memory.oom.group fastställa att hela gruppen avslutas i en nödsituation – det är bättre än en halvdöd restprocess.

Verktyg och mätvärden i praktiken

För att snabbt kunna utreda orsakerna samlar jag in reproducerbara siffror. Dessa verktyg är till stor hjälp för mig:

  • Processöversikt: ps -eo pid,ppid,cmd,%mem,rss --sort=-rss | head visar program som tar upp mycket minne.
  • Smaps-sammanfattning: cat /proc//smaps_rollup returnerar RSS/PSS/Swap för en process utan omfattande parsning.
  • pmap: pmap -x | sort -nrk3 | head visar mappningar med storlek och RSS, vilket är bra för heap och stora segment.
  • Användning av betongplattor: slabtop -o visar kärncacher som kan svälla vid hög belastning.
  • Systemtryck: vmstat 1 och sar -r 1 ger en bakgrund till paging, swap-I/O och frees.
  • Cgroup-statistik: I v2 kontrollerar jag /sys/fs/cgroup/memory.current, memory.swap.current och memory.stat för den berörda tjänsten.
Läs # OOM-loggar på ett smidigt sätt
dmesg -T | egrep -i 'out of memory|oom-kill|killed process'

# Sortera de bästa kandidaterna efter 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

Om jag upprepade gånger ser OOM-fel dokumenterar jag dessa Baslinje dessa värden under normal drift och jämför dem med händelsefönstret. Avvikelser märks omedelbart, till exempel en okontrollerad ökning av PSS eller oproportionerligt stora slabs.

Systemd, containrar och orkestrering: målinriktad styrning

I systemd ställer jag in prioriteringar och gränser deklarerad i Unit-filer:

[Service]
# Skydda processen eller gör den avstängningsbar
OOMScoreAdjust=-900

# Hårda/mjuka minnesgränser (cgroup v2)
MemoryMax=8G
MemoryHigh=6G
# Valfritt: Begränsa swap
MemorySwapMax=2G

# Beteende vid OOM under systemd
# (t.ex. tvinga omstart)
Restart=on-failure
RestartSec=5

I container-miljöer ser jag till att det finns tydliga gränsvärden för varje tjänst. För mig är det viktigt att skilja mellan Begäran (planerad bokning) och Begränsa (hård övre gräns). Pods med lämpliga förfrågningar/begränsningar får bättre QoS-klassificeringar; „BestEffort“-arbetsbelastningar riskerar att drabbas av OOM. En praktisk detalj: Om kärnan avslutar en container på grund av Cgroup-OOM ser jag ofta avslutningskoden 137 och händelser med OOMKilled; i värd-dmesg Det går att koppla ihop dessa faktorer. I produktiva kluster planerar jag kritiska driftsättningar som „Guaranteed“, medan batchjobb medvetet körs med kortare tidsintervall och därmed ger vika först.

Kärndetaljer: OOM Reaper, THP och fragmentering

Efter dödandet griper OOM Reaper: en kärntråd avlägsnar så snabbt som möjligt minnesmappningen från offerprocessen, så att RAM-minnet verkligen frigörs. Det förklarar varför minnet ibland först till syns tydligt i Kille-posten. Parallellt med detta kan Lagringskomprimering stöter på begränsningar – om RAM-minnet är starkt fragmenterat saknas sammanhängande områden för stora allokeringar (till exempel med Transparent Huge Pages, THP). THP ger prestanda, men kan under hög belastning försvåra tilldelningar. För latenskritiska arbetsbelastningar inaktiverar eller begränsar jag THP på prov och mäter effekterna.

En annan faktor är Slab-cacher och sidcachen: Vid I/O-intensiva arbetsbelastningar växer dessa cacher kraftigt. Med vm.vfs_cache_tryck och genom målinriktad återvinning kan man styra deras andel; generell tömning (Drop-Caches) använder jag högst som ett diagnostiskt verktyg, inte som en långsiktig lösning. Dessutom är jag noga med att NUMA: Om en minnesnod är full kan en process i den här NUMA-zonen misslyckas trots att det finns ledigt RAM-minne globalt. Motsvarande meddelanden visas även i kärnloggarna.

Fördjupa kunskaperna om swap-strategier: Swappiness, ZRAM/Zswap, I/O-budget

Swap är inget ont, utan ett Stötdämpare. Det avgörande är att använda den på ett klokt sätt. Med vm.swappiness justerar jag hur tidigt kärnan flyttar data till swap. För låga värden gör att sidcachen dominerar och kan utlösa OOM-fel tidigare; för höga värden förskjuter belastningen till swap-I/O och gör systemet trögt. I kompakta värddatorer använder jag gärna ZRAM eller . Zswap, för att skapa en komprimerad buffert som fångar upp toppar utan att överbelasta hårddisken. Det är viktigt att komma ihåg att swap inte ersätter bristande kapacitet. Det köper bara tid så att OOM-killer inte behöver träda i funktion överhuvudtaget.

Särskilda fall: mlock, RLIMITS, fallgropar med överallokering

Vissa randvillkor ökar risken för OOM eller förändrar beteendet:

  • Spärrad lagringsplats: Processer som sker via mlock() Genom att fästa sidor undanhåller man dem från Reclaim. Vid hög frekvens kan Reapers tempo bromsas upp.
  • RLimits: RLIMIT_AS och RLIMIT_RSS fastställer övre gränser per process och förhindrar att enskilda tjänster tar för mycket resurser i anspråk – en del av lösningen mot OOM-fel.
  • Överengagemang: Alltför generösa inställningar för överallokering möjliggör ett stort virtuellt adressutrymme som senare inte kan täckas fysiskt. Just allokeringsspikar från många trådar leder då samtidigt till felallokeringar och påskyndar OOM-händelser.
  • panic_on_oom: För mycket kritiska system finns möjligheten att vid OOM reagera med en kernelpanic. Detta är endast meningsfullt i noggrant definierade HA-scenarier och är i övrigt kontraproduktivt.
  • „Unkillbar“ är riskabelt: oom_score_adj=-1000 Det skyddar visserligen mot ”Killer”, men kan blockera hela systemet. Jag använder det endast för absolut nödvändiga, små processer (t.ex. init), inte för minneskrävande servertjänster.

I praktiken: Fastställa prioriteringar och säkerställa förändringar

Jag definierar inom teamet en Rankning Tjänsterna: Vad måste finnas kvar, vad kan tas bort först? Denna ordning översätter jag till oom_score_adj, Cgroup-gränser och (där det är tillgängligt) omstartspolicyer. Ändringarna införlivas som kod i enhets- eller distributionsmanifest, åtföljda av mätpunkter i övervakningen. I belastningstester simulerar jag lagringsbelastning: jag ökar datamängderna, höjer parallelliteten, låter cachen växa – och observerar om just de „offerbara“ processerna faller bort, medan kärnkomponenterna förblir online. Först när detta fungerar på ett reproducerbart sätt tas konfigurationen i drift.

Diagnosmönster: Att skilja mellan läckor, heap-fel och fragmentering

Inte varje stigande RSS-värde är en läcka. Jag gör en systematisk åtskillnad:

  • Läcka: RSS/PSS ökar monotont, även utan ökande belastning; smaps_rollup växer jämnt, GC-cykler (vid Managed Runtimes) hjälper inte.
  • Heap-toppar: RSS ökar med belastningen och sjunker sedan igen; sidcachen korrelerar med I/O-mönstren.
  • Fragmentering: Tillräckligt med ledigt RAM-minne, men stora allokeringar misslyckas; loggarna visar försök till komprimering, THP-tilldelningar misslyckas oftare.

För JVM- eller Node-arbetsbelastningar kontrollerar jag om runtime-miljön känner av containergränserna. För stort dimensionerade heap eller JIT-kodcacher kan överskrida gränsen och orsaka OOM-fel, även om det till synes fortfarande finns utrymme kvar. Jag ställer in heap, inklusive overhead, så att under MemoryMax det finns fortfarande utrymme kvar för Native-andelar, trådstaplar och sidcache.

Handbok för driftsättningar och belastningstester under lagringstryck

  1. Mäta baslinjen: RSS/PSS per tjänst, slab-andelar, swap-kvot, cache-storlekar, oom_score.
  2. Att sätta gränser: Minne hög/max eller fastställa gränser för containrar med en rimlig marginal; OOMScoreAdjust tilldelas efter prioritet.
  3. Skapa stress: Datavolym, samtidighet, cachetillväxt; notera I/O- och CPU-profiler.
  4. Observera: dmesg -T, värd- och cgroup-mått; kontrollera vem som först hamnar under press.
  5. Iterera: Justera gränsvärden/Adj, anpassa swappiness, testa THP-inställningarna, mät på nytt.
  6. Automatisera: Integrera kontroller i CI/CD, larm vid gränsvärden, omstartspolicyer för drabbade tjänster.

I korthet: konkreta åtgärder

Jag uppfattar OOM Killer som Signal, att mitt system tidigare hade för liten buffert eller att processerna prioriterades felaktigt. Med övervakning, realistiska gränsvärden, en väl genomtänkt swap-strategi och en medveten användning av oom_score_adj Jag minskar avbrott avsevärt. I produktiva miljöer skyddar jag kärnprocesser, gör perifera tjänster överflödiga och mäter varje förändring. När det gäller containrar fastställer jag strikta Cgroup-gränser så att en tjänst inte blockerar hela värdsystemet. Den som upprätthåller denna disciplin ser till att Linux förblir responsivt även under press och förkortar tiden det tar att hitta orsaken avsevärt.

Aktuella artiklar

Serverrack med Linux-system och visualiserad lagringsutnyttjande
Servrar och virtuella maskiner

Att förstå OOM Killer: När Linux avslutar processer

Lär dig hur OOM-killer i Linux fungerar vid minnesbrist, hur den avslutar processer och hur du som administratör i webbhotellsmiljöer kan undvika minnesbristproblem med hjälp av nyckelordet oom killer linux.

Administratören analyserar Journalctl-loggar på en Linux-server i datacentret
Administration

Att använda journalctl effektivt: Felanalys på Linux-servrar

Lär dig hur du använder `journalctl` för effektiv felanalys på Linux-servrar. Med hjälp av filter för tid, tjänst och prioritet kan du analysera Linux-loggar på ett strukturerat sätt och optimera felsökningen på dina servrar.