Memory Pressure im Linux-Kernel trifft Hosting-Systeme direkt: Steigt der Druck, verschieben sich CPU-Zeit und I/O auf aufwendiges Reclaim, Antwortzeiten klettern und OOM-Risiken nehmen zu. Ich zeige klar, wie ich Memory-Druck erkenne, messe und entschärfe, damit Hosting-Workloads konstant reagieren.
Zentrale Punkte
Ich konzentriere mich auf die entscheidenden Stellschrauben, die in Hosting-Umgebungen über Leistung und Ausfälle entscheiden. Die folgenden Punkte bilden den roten Faden, an dem ich Diagnose und Optimierung ausrichte. Mit diesem Überblick vermeide ich Fehlinterpretationen von „voller RAM“ und erkenne echten Druck rechtzeitig.
- PSI-Metriken zeigen Wartezeiten statt bloßer Belegung und decken Verzögerungen früh auf.
- Swap-Last signalisiert Reclaim-Probleme, die I/O und Latenzen verstärken.
- Cgroups-Grenzen steuern Drosselung, Schutz und OOM-Verhalten pro Service.
- Cache-Verdrängung beeinflusst Web- und Datenbank-Performance unmittelbar.
- Kapazitätsplanung und Tuning halten Headroom und vermeiden Thrashing.
So strukturiere ich meine Analysen vom Kernel bis zur Applikation und setze passende Maßnahmen priorisiert um. Der Fokus bleibt auf messbaren Effekten, nicht auf Ratearbeit.
Was bedeutet Memory Pressure im Linux-Kernel?
Memory-Druck bedeutet, dass der Kernel spürbar Zeit dafür aufwendet, Speicher freizuräumen, statt Arbeit der Nutzerprozesse fortzusetzen; die CPU rechnet dann vermehrt an Scans, Writes und Evictions, während Anfragen warten. Ich unterscheide klar zwischen „voller RAM“ und „fehlt nutzbarer Headroom“: Ein „voller“ Cache ist gesund, Engpässe entstehen erst, wenn Reclaim-Investitionen hochgehen. Der Kernel scannt inaktive Listen, schreibt dirty-Seiten heraus, wirft Dateicache weg und lagert anonyme Seiten aus, sobald Watermarks unterschritten werden. Entscheidend ist die aufgewendete Zeit für diese Tätigkeiten; sie spiegelt sich in Wartephasen der Tasks und verlängerten Antwortzeiten. Ein Host kann bei 95 % Nutzung leise laufen, solange Cache leicht zurückgewinnbar ist, aber bei niedriger Auslastung heftig stallen, wenn aktive anonyme Seiten verdrängt werden müssen.
PSI verstehen und messen
Pressure Stall Information (PSI) macht Memory Pressure greifbar, weil ich nicht die Belegung, sondern Verzögerungen messe. In /proc/pressure/memory sehe ich „some“ und „full“: „some“ beschreibt Zeiten, in denen mindestens eine Aufgabe auf Speicher wartet, „full“ signalisiert Phasen, in denen alle zusammenstehen. Beispiel: „some avg10=4.67“ bedeutet, dass in den letzten 10 Sekunden 4,67 % der Zeit Stalls durch Speicherengpässe auftraten; „full avg10=0.30“ weist auf seltene Vollbremsungen hin. Ich korreliere steigende „some“-Werte früh mit Antwortzeiten und skaliere, tune oder entlaste, bevor harte OOMs erscheinen. Diese Sicht verhindert, dass ich mich von scheinbar „hohem freien“ RAM täuschen lasse, denn freie Seiten ohne schnelle Reclaim-Möglichkeit nützen wenig.
| Messgröße | Orientierungswert | Symptom | Aktion |
|---|---|---|---|
| PSI memory some (avg10) | > 2–3 % anhaltend | Antwortzeiten steigen | RAM-Headroom prüfen, Cgroup-Limits feinjustieren |
| PSI memory full (avg10) | > 0,1 % spürbar | Kurze Totzeiten | Ursache identifizieren, Thrashing beenden |
| MemAvailable | < 10 % des RAM | Geringer Puffer | Cache/Workload entlasten, Kapazität planen |
| vmstat si/so | dauerhaft > 0 | Swap-Pressure | Swappiness/Swap anpassen, Hotset schützen |
Symptome in Hosting-Umgebungen
Auf betriebsamen Hosts sehe ich zuerst Latenzspitzen, während die CPU-Last scheinbar moderat bleibt; der Kernel steht in Reclaim-Schleifen, I/O staut sich und Anfragen warten. Der Load Average klettert, obwohl Kerne frei wirken, weil viele Tasks auf Speicher oder I/O blockieren; das ist ein Kernzeichen erhöhten Drucks. Anhaltende si/so-Werte in vmstat zeigen, dass das System aktiv swappt, was TLS-Handshakes, dynamische Inhalte und Query-Pfade bremst. Bleibt das unbehandelt, kippt das System in Thrashing: Die CPU verbringt einen Großteil der Zeit mit Paging und Swapping, statt nützliche Arbeit zu erledigen. In der Eskalation greift der OOM-Killer ein und beendet Prozesse mit hohem Score; eine gezielte Analyse des OOM-Killers hilft mir, Muster und Fehlkonfigurationen zu erkennen.
Relevanz für Hosting-Workloads und Cgroups
In gemeinsam betriebenen Umgebungen reichen einzelne speicherhungrige Anwendungen aus, um Latenzen für viele Kunden zu erhöhen; Cgroups lindern die Auswirkungen, aber sie retten keine falsch dimensionierten Instanzen. In VPS und Cloud-Instanzen führt knapp bemessener RAM oder eine schlechte Swap-Strategie schneller zu Druckspitzen; Isolation schützt andere, nicht jedoch den eigenen Dienst. Datenbanken leben von großen Buffer-Pools; wenn Reclaim diese verdrängt oder Swap eingreift, steigen Query-Zeiten und die Durchsatzrate sinkt signifikant. Container-Orchestrierungen nutzen memory.low, memory.high und memory.max, um wichtige Services zu schützen, Stalls zu drosseln und im Notfall gezielt zu beenden. Ich wähle daher Limits bewusst und beobachte PSI je Service, um rechtzeitig gegenzusteuern und Reserven für kritische Workloads freizuhalten.
Monitoring-Strategie und Metriken
Ich lese MemAvailable, Buffers und Cached, um zu verstehen, wie viel Speicher kurzfristig zurückgewinnbar ist; reine MemFree-Werte führen leicht in die Irre. Parallel schaue ich vmstat: Anhaltende si/so-Werte deuten auf Swap-Druck, der I/O gewaltig anfeuert und Latenzen hochtreibt; für Hintergründe zur Swap-Nutzung nutze ich bewährte Diagnosemuster. PSI liefert mir den fehlenden Baustein, weil „some“ und „full“ echte Verzögerungen quantifizieren; ich alarme bei Schwellen und trenne Lastspitzen von chronischen Engpässen. Zeitreihen über sar oder den Observability-Stack machen Muster sichtbar und helfen mir, Tuning-Erfolge zu bestätigen. dmesg verrät OOM-Ereignisse, die auf harte Grenzen oder Fehlkonfigurationen hinweisen; so baue ich ein stimmiges Bild aus Kernel-Sicht, I/O-Verhalten und Applikation.
Typische Workloads unter Druck
Webserver wie Nginx oder Apache liefern Inhalte langsamer aus, wenn Reclaim und Swap im Hintergrund laufen; Keep-Alive-Verbindungen bleiben länger offen, was Warteschlangen verstärkt. PHP- und Python-Stacks belegen RAM durch Framework-Caches, JIT-Anteile und Session-Daten; bei Verdrängung pendeln diese Daten zwischen RAM und Storage und verlängern Antworten deutlich. Datenbanken verlieren Tempo, sobald Buffer-Pools schrumpfen oder Teile auf Swap landen; selbst geringe zusätzliche Latenz pro I/O summiert sich bei vielen Queries. Caching-Dienste wie Redis oder Memcached brauchen RAM-Treffer; landen Schlüsselbereiche auf Swap, ist der Vorteil dahin und das Risiko steigt, bei Druck beendet zu werden. In allen Fällen liefern PSI und Swap-Metriken die klarsten Hinweise, dass Speicher zum Engpass wurde und nicht die CPU.
Systemtuning und Kernel-Parameter
Ich beginne bei vm.swappiness: Eine moderat reduzierte Einstellung verhindert übermäßigen Swap-Einsatz, ohne notwendiges Reclaim zu blockieren; ich messe Effekte konsequent mit PSI. Danach optimiere ich vm.dirty_ratio und verwandte Limits, damit ich keine langen Flush-Wellen auslöse und trotzdem keine Kleinstschreiborgien provoziere; beides hat spürbare Auswirkungen auf Latenzen. Auf Cgroups v2 setze ich memory.low für kritische Dienste, memory.high für Drosselung bei Überlast und memory.max als harte Grenze mit kontrollierbaren OOMs. NUMA-Topologien beachte ich besonders: Lokaler Druck kann entstehen, obwohl global noch RAM frei ist; Prozess- und Speicherbindung entschärfen solche Fallen. Schließlich prüfe ich Page-Cache-Verhalten; unnötige Verdrängung drückt Hit-Raten und kostet bei Web- und DB-Workloads unmittelbar Zeit, wozu die Page-Cache-Optimierung hilfreiche Einsichten liefert.
Tiefer Blick in Reclaim-Pfade
Um Maßnahmen treffsicher zu wählen, unterscheide ich kswapd und Direct Reclaim. kswapd arbeitet asynchron, wenn Watermarks unterschritten werden; er ist relativ sanft, solange genügend leicht reclaimbarer Cache existiert. Direct Reclaim greift synchron in Ausführungskontexte ein, wenn Threads akut Seiten brauchen – hier entstehen die für Nutzer spürbaren Stalls. Ich beobachte, ob Reclaim eher Dateicache oder anonyme Seiten trifft: Verdrängt der Kernel primär Dateicache, steigen Cache-Misses; verdrängt er anonymen Speicher (z. B. Heap), drohen harte Stopps und Swap-Aktivität. Moderne Workingset-Mechanismen berücksichtigen Refault-Distanzen, um nützliche Seiten länger zu halten; wenn ich trotzdem viele wiederholte Refaults sehe, weiß ich, dass Hotsets größer als der verfügbare Headroom geworden sind.
Zusätzlich beachte ich Kompaktierung und Defragmentierung: kcompactd versucht, zusammenhängende Bereiche zu schaffen, etwa für große Allokationen oder THP. Bleibt die Kompaktierung hinterher, sehe ich erhöhte CPU-Anteile in kcompactd, steigende Latenzen und erhöhte „full“-PSI-Anteile bei Lastspitzen. In solchen Fällen ist es oft sinnvoller, Druck zu senken oder THP-Politiken anzupassen, statt nur „mehr swap“ zu geben.
Swap-Strategien im Detail
Swap ist kein Feind, sondern ein Werkzeug – falsch eingesetzt jedoch ein Latenzverstärker. Ich differenziere:
- Kein Swap: Sicher gegen Swap-Lags, aber riskant bei Peaks – OOMs kommen früher, Reclaim hat keinen Ausweichpuffer.
- Moderater Swap auf schneller SSD: Gut, um kalte anonyme Seiten auszulagern; schützt Hotsets im RAM, wenn swappiness und cgroup-Grenzen klug gesetzt sind.
- zswap/zram: Kompression entlastet I/O; geeignet für I/O-ärmere Hosts oder als Puffer gegen kurzzeitige Druckspitzen. Ich prüfe CPU-Budget und Kompressionsratio, um nicht CPU-gebremst zu werden.
Ich wähle swappiness nicht pauschal niedrig; bei Workloads mit großem Dateicache ist eine etwas höhere swappiness sinnvoll, um kalte anonyme Seiten wegzudrücken und Dateicache stabil zu halten. Kritische Services (z. B. Datenbanken) schütze ich mit memory.low und ggf. mit Locking ihrer Hotsets im RAM, damit Swap nicht das Falsche trifft. Entscheidend ist, dass vmstat si/so und PSI konsistent fallen, wenn ich die Strategie anpasse; sonst korrigiere ich nach.
THP, Kompaktierung und Fragmentierung
Transparent Huge Pages (THP) sparen TLB-Hits und helfen CPU-lastigen, speicherintensiven Anwendungen. Unter Druck verursachen sie aber Kompaktierungsarbeit; „always“ kann dann zu großen Stalls führen. Ich nutze „madvise“ gezielt für Workloads, die profitieren (z. B. bestimmte In-Memory-Engines), und lasse bei latenzsensiblen Web-Stacks THP eher gezielt deaktiviert oder nur per madvise zu. Ergänzend beobachte ich vm.compaction_proactiveness und prüfe, ob proaktive Kompaktierung Stalls verschiebt oder wirklich reduziert. Wenn THP-Seiten oft zerschnitten werden oder Kompaktierung heiß läuft, spricht das für zu wenig Headroom oder unpassende Allokationsmuster in der Applikation.
NUMA-Fallen und Lokaldruck
Auf NUMA-Hosts ist globaler „freier RAM“ trügerisch: Ein Socket kann unter Druck stehen, während ein anderer ungenutzt bleibt. Ich prüfe NUMA-Statistiken und verankere Prozesse lokal (CPU-/Memory-Bindung), damit Hotsets nahe an der Rechenlast bleiben. Direct Reclaim auf einem Knoten trotz globaler Reserven signalisiert NUMA-Ungleichgewichte; hier helfen interleaved Allokationen für breit gestreute Services oder strikte Bindung für monolithische Workloads. PSI pro cgroup kombiniert mit NUMA-Statistiken zeigt mir, ob ein einzelner Node die Warteschlangen erzeugt.
Applikationsnahe Maßnahmen
Ich analysiere Speicherprofile mit ps, top, htop und Profiler-Werkzeugen, um echte Speicherfresser und Leaks zu finden; dabei beachte ich, wie sich Hotsets im Zeitverlauf verändern. Anwendungscaches wähle ich bewusst: Zu groß erzeugt Druck, zu klein verschenkt Tempo; ich justiere mit Blick auf PSI und Antwortzeiten, nicht auf Bauchgefühl. Bei erkannten Drucksignalen kann die Anwendung weniger kritische Caches oder temporäre Daten freiwillig freigeben; so reduziere ich Stalls, ohne globale Limits anzufassen. Startparameter und GC-Tuning (z. B. für JVMs) passe ich so an, dass sich Working Sets sauber im RAM halten; aggressive Allokationsmuster entschärfe ich durch Batching. Auch Build-Artefakte und Debug-Symbole behalte ich im Blick, denn übersehene Reste kosten still Speicher und erhöhen das Risiko späterer Stalls.
Cgroups v2-Finessen und OOM-Strategien
Mit Cgroups v2 trenne ich Schutz, Drosselung und harte Grenzen sauber: memory.low reserviert Headroom für kritische Services; anderer Reclaim geht an weniger wichtige Gruppen. memory.high drosselt bei Überschreitung durch gezieltes Throttling und zwingt Applikationen, Speicher freizugeben, bevor das System leidet. memory.max ist die letzte Verteidigungslinie – überschritten bedeutet OOM im kontrollierten Rahmen. Ich richte PSI pro cgroup ein, damit Alarme dort auslösen, wo Stalls entstehen; globaler PSI bleibt ruhig, während ein einzelner Dienst kollabiert – genau dieses Muster will ich erkennen. Zusammen mit OOM-Prioritäten setze ich klare Opferregeln: unwichtige Batch-Worker sterben zuerst, Kern-APIs behalten ihren Headroom.
Virtualisierung: Ballooning, KSM und Overcommit
In virtualisierten Umgebungen treffe ich auf doppelten Druck: Der Gast sieht scheinbar freien RAM, während der Hypervisor via Ballooning entzieht. Dieses Spiel erhöht Reclaim-Kosten beiderseits. Ich messe PSI im Gast und korreliere mit Hypervisor-Metriken; steigt PSI bei Ballooning-Events, braucht die VM mehr zugesicherte Kapazität oder bessere Cgroup-Policen im Host. KSM spart RAM durch Deduplizierung identischer Seiten, kostet aber CPU; in Hosting-Setups mit vielen gleichartigen VMs kann es lohnen, solange die zusätzliche CPU-Last die SLOs nicht gefährdet. Overcommit (z. B. aggressive Zuteilung vieler kleiner VMs) plane ich nur mit festen SLO-Reserven und striktem memory.low für latenzkritische Systeme.
Architekturentscheidungen im Hosting
Ich setze auf horizontale Verteilung, damit einzelne Instanzen weniger Druckspitzen abbekommen; skalierbare Pools dämpfen Ausreißer und halten Latenzen enger. Rollen trenne ich klar: Datenbanken, Applikation und Caching bekommen eigene Ressourcenpools, damit Reclaim-Prozesse keine unerwarteten Nebenwirkungen über Systemgrenzen erzeugen. Storage wähle ich mit Blick auf Schreiblatenz, denn Dirty-Page-Flushes schlagen direkt auf Antwortzeiten durch; ein schneller Pfad reduziert spürbar Reclaim-Zeiten. In Clustern plane ich RAM-Reserven pro Knoten ein und steuere über Scheduler-Policies, damit Last und Speicherverbrauch gleichmäßiger verteilt bleiben. Ich automatisiere Skalierung mit PSI-Schwellen, damit steigende „some“-Werte Aktionen auslösen, bevor es zu Vollbremsungen und Kill-Ereignissen kommt.
Kapazitätsplanung und Headroom-Modelle
Headroom definiere ich messbar: Ich halte ausreichend Reserven, damit „some“-PSI unter normalen Peaks unter definierten Schwellen bleibt und „full“ praktisch nicht auftritt. Dafür nutze ich Perzentile (z. B. 99. Perzentil der stündlichen Last) und plane 10–30 % zusätzlichen RAM je nach Volatilität des Workloads. Datenbanken bekommen größere feste Reserven, während Web-Frontends dynamischer skalieren. Ich kalibriere Rebound-Zeiten: Wie schnell fallen PSI und si/so nach einem Peak? Bleiben sie erhöht, ist das ein Zeichen für zu geringe Reserven oder eine unpassende Swap-/Dirty-Strategie. So wird Kapazitätsplanung zum kontinuierlichen Prozess statt zur jährlichen Schätzung.
Alarmierung und SLO-gesteuertes Tuning
Ich verknüpfe PSI mit Nutzer-SLOs: Steigt „some avg10“ gleichzeitig mit Latenzen der API, greife ich ein. Alarme staffele ich in „gelb“ (anhaltend 2–3 % „some“, „full“ nahe 0) und „rot“ (über 5 % „some“ oder „full“ > 0,1 %). cgroup-basierte Alarme helfen, die laute Minderheit zu isolieren. Zusätzlich alarme ich auf steigende Dirty-Queues und Schreibwartezeiten, damit ich Dirty-Wellen rechtzeitig glätte. Ziel ist, dass Tuning-Maßnahmen (Swappiness, memory.high, Cachegrößen) beobachtbar und reversibel sind; ich rolle Änderungen schrittweise aus und vergleiche Vorher/Nachher über dieselben Metriken.
Schritt-für-Schritt-Diagnose im Alltag
Ich prüfe zuerst free -h und MemAvailable: Fällt der Wert deutlich, suche ich nach Caches, die sich sinnvoll zurückgeben lassen, und nach Services mit wachsenden Hotsets. Danach lasse ich vmstat mit kurzem Intervall laufen, um si/so-Trends zu erkennen; anhaltendes Swapping bestätigt Druck und führt mich zum I/O-Pfad. Anschließend lese ich /proc/pressure/memory und werte „some“ und „full“ über 10, 60 und 300 Sekunden aus; steigende Mittelwerte verknüpfe ich direkt mit beobachteten Latenzen. dmesg zeigt mir OOM-Spuren und verrät, welche Prozesse zuletzt Speicherkrisen ausgelöst oder abbekommen haben; daraus leite ich Limits und Prioritäten für Cgroups ab. Aus all dem forme ich eine Hypothese, setze kleine Tuning-Schritte um, verifiziere per PSI und behalte die Antwortzeit im Blick.
Runbooks und typische Ursachenketten
Einige Muster begegne ich immer wieder:
- Backup- oder Scan-Jobs verdrängen Page-Cache: Plötzlich sinken Cache-Hitraten, Web/DB werden langsamer. Maßnahme: Jobs drosseln (I/O-Priorität), Zeitfenster verschieben, memory.high für die Job-cgroup setzen, Page-Cache-Budget der kritischen Dienste schützen.
- Lecks in Worker-Prozessen: Langsam steigender anonymer Speicher, PSI „some“ klettert über Stunden. Maßnahme: Leck identifizieren, Autorestart-/Recycling-Policies einführen, Memory-Limits setzen, damit Lecks nicht den gesamten Host gefährden.
- THP-bedingte Stalls: kcompactd-Last steigt unter Traffic-Peaks. Maßnahme: THP auf „madvise“, betroffene Services anpassen, Kompaktierungs-Parameter prüfen, Headroom erhöhen.
- NUMA-Lokaldruck: Ein Socket thrash’t, obwohl global RAM frei ist. Maßnahme: Affinitäten korrigieren, Interleave für breit gestreute Workloads, Lastverteilung im Scheduler anpassen.
- Swap auf langsamen Disks: si/so steigt, Antwortzeiten explodieren. Maßnahme: Swap auf schnelleren Storage verlegen, zswap/zram evaluieren, swappiness und cgroup-Policen nachschärfen.
Jedes Runbook endet mit Validierung: Fallen „some/full“ und stabilisieren sich Latenzen? Wenn nicht, war die Annahme falsch oder unvollständig – dann iteriere ich weiter.
Werkzeuge und Tracing im Betrieb
Neben klassischen Tools setze ich auf tiefergehende Sicht: Ich beobachte Page-Cache- und Anon-Verhältnisse, Page-Fault-Raten, Refault-Muster und Writeback-Queues. eBPF-/Tracing-Ansätze liefern mir punktgenau, wo Wartezeiten entstehen – etwa entlang der Reclaim-Pfade, im Writeback oder bei Allokationen großer Blöcke. Wichtig ist mir eine sparsame, produktionstaugliche Instrumentierung: kurze Aktivierungsfenster, Sampling statt Dauerfeuer und klare Korrelation mit Applikationsmetriken. So finde ich Ursachen, bevor ich großflächig an Parametern drehe.
Kernpunkte und nächste Schritte
Memory Pressure beschreibt die verlorene Zeit durch Speicherknappheit, nicht bloß belegten RAM; ich messe sie mit PSI, erkenne Trends früh und handele datenbasiert. Wer MemAvailable, vmstat si/so, PSI und dmesg zusammendenkt, findet die wahren Ursachen für Latenzspitzen und Thrashing. Mit Swappiness-, Dirty- und Cgroup-Tuning reduziere ich Stalls gezielt und sichere wichtigen Diensten ihren Headroom. Auf Architektur-Ebene dämpfen horizontale Verteilung, klar geteilte Rollen und schnelle Storage-Pfade die Folgen jeder Druckspitze. Am Ende zählt, dass ich Diagnose und Gegenmaßnahmen kontinuierlich verknüpfe: messen, anpassen, erneut messen – bis Leistung und Stabilität wieder passen.


