Speicherlecks unter dem Mikroskop: Was im RAM passiert, wenn ein Server zu lange läuft
Die Symptome dieses technischen Problems sind jedem Besitzer eines Game-Servers bekannt: Unmittelbar nach einem Neustart läuft die Spielwelt einwandfrei – die Server-Tickrate (TPS) bleibt auf dem Maximum und die Latenz є minimal. Doch etwa 12 Stunden später beginnt die Umgebung ohne ersichtlichen Grund zu stottern, und nach einigen weiteren Stunden beendet das Hosting-Betriebssystem den Prozess vollständig mit einer knappen, aber fatalen Meldung: Out Of Memory (OOM).
Viele schieben dies auf eine schlechte Optimierung der Spiel-Engine selbst. Um Ihr Projekt jedoch effektiv vor solchen Abstürzen zu schützen, müssen wir die Funktionsweise des Arbeitsspeichers (RAM) unter das Mikroskop nehmen. Es gilt zu verstehen, wie eine Game-Engine flüchtige Sektoren aufteilt, wie der integrierte Garbage Collector arbeitet und warum versteckte Lecks im Code entstehen.
1. Die zwei Säulen des Speichers: Stack vs. Heap
Wenn ein Game-Server ausgeführt wird, weist das zugrunde liegende Betriebssystem ihm einen isolierten Speicherbereich im RAM zu. Die Spiel-Engine (sei es Java, C# oder C++) unterteilt diesen Speicher in zwei grundlegend verschiedene Bereiche: den Stack und den Heap.
Der Stack — Schnelle, temporäre Zuweisung
Der Stack arbeitet strikt nach dem Last-In, First-Out (LIFO)-Prinzip und wird automatisch von der CPU-Architektur auf Hardware-Ebene verwaltet. Er speichert einfache, primitive Datentypen und lokale Variablen, die innerhalb aktiver Funktionen deklariert werden (z. B. die exakten 3D-Koordinaten eines Projektils im Moment des Abschusses oder die temporäre Dialog-ID eines Menüs).
Sobald diese Funktion ihre Ausführung beendet hat (das Projektil trifft eine Wand, das Dialogfenster schließt sich), werden die im Stack zugewiesenen Zellen augenblicklich und deterministisch geleert. Der Stack arbeitet blitzschnell, aber seine Gesamtkapazität є streng begrenzt.
Der Heap — Globales, dynamisches Chaos
Der Heap repräsentiert einen riesigen, unstrukturierten Pool des Arbeitsspeichers, der dafür ausgelegt є, schwere, langlebige und sich dynamisch verändernde Objektgraphen aufzunehmen. Absolut jede persistente Entität innerhalb der Spielwelt – komplexe, von Spielern gebaute Strukturen, vollständige Inventar-Arrays für Hunderte von Profilen, die KI von Bots und Datenbank-Caches – lebt ausschließlich im Heap.
Im Gegensatz zum Stack können sich Datensektoren im Heap nicht automatisch leeren, wenn eine Routine beendet wird. Wenn ein Spieler eine Lagerkiste öffnet, instanziiert der Server ein Array mit diesen Item-Definitionen im Heap. Schließt der Benutzer das Inventar, wird dieses Array nicht mehr benötigt, bleibt jedoch so lange im Heap liegen, bis der Anwendungscode explizit dessen Löschung befiehlt.
2. Wie der Garbage Collector arbeitet
In höheren, verwalteten Programmiersprachen wie Java (Minecraft) oder C# (Unity / Rust-Umgebungen) müssen Entwickler nicht für jede einzelne Heap-Entität manuelle Löschbefehle schreiben. Diese Aufgabe übernimmt eine im Hintergrund laufende Bereinigungsroutine namens Garbage Collector (GC).
In regelmäßigen Abständen wacht der GC-Thread auf und scannt den Heap. Er sucht nach Objektinstanzen, die keine aktiven Referenzpfade mehr aufweisen, die auf den primären Spiel-Loop verweisen. Verlasst ein Spieler den Server, verliert sein zugehöriges Player-Objekt die Verbindung zur aktiven Simulation. Der GC erkennt dieses verwaiste Element, löscht dessen Byte-Fußabdruck und gibt den RAM-Bereich wieder frei.
Warum der Garbage Collector Stottern verursacht (Stop-the-World)
Um den Heap sicher zu scannen und zu bereinigen, ohne Datenkorruptionen zu riskieren, muss eine klassische Garbage-Collector-Engine die primäre Spielsimulation für den Bruchteil einer Millisekunde vollständig einfrieren. Dieser Vorgang wird technisch als Stop-the-World (STW)-Phase klassifiziert. Wenn der Heap Ihres Servers auf 10–16 GB angewachsen є und mit Millionen von kleinen Objektreferenzen gefüllt є, benötigt der GC zu viel Zeit, um den Speicherbaum zu analysieren. Für die aktiven Spieler äußert sich dies alle paar Minuten in plötzlichen Mikrorucklern oder Server-Stottern.
3. Anatomie eines Speicherlecks (Memory Leak)
Ein Speicherleck in modernen Game-Server-Umgebungen bedeutet nicht, dass RAM-Zellen physisch verschwinden; vielmehr beschreibt es einen Zustand, in dem der Garbage Collector daran gehindert wird, ein unbenötigtes Objekt zu löschen, weil ein aktiver Codeabschnitt fälschlicherweise weiterhin eine Referenz darauf hält.
Betrachten wir ein klassisches Beispiel für ein verstecktes Speicherleck in benutzerdefinierten Skripten:
- Ein Entwickler schreibt ein Logging-Skript, das administrative Spieleraktionen in einem globalen Array (einer Liste) im Speicher protokolliert.
- Ein Spieler führt eine Aktion aus, der Datenstring wird dem Array hinzugefügt und der Nutzer verlässt anschließend den Server.
- Die Spiellogik löscht das lokale Spielerobjekt, aber der Log-Eintrag, der eine Referenz auf die Identität des Spielers enthält, bleibt in diesem globalen Array verschachtelt.
- Da diese globale Liste persistent є und vom Skript niemals geleert wird, stellt der Garbage Collector fest: Ein aktiver Datenknoten verweist immer noch auf die Spielerattribute. Folglich entscheidet der GC, dass dieser Speicherbereich wichtig є, und löscht ihn nicht.
Über einen Zeitraum von 24 Stunden verbinden sich Tausende von Spielern mit dem Server. Der Heap füllt sich mit Gigabytes an „Geisterdaten“, die für das eigentliche Gameplay längst tot sind, aber aufgrund von Architekturfehlern in Mod-Skripten im RAM blockiert bleiben.
4. Der finale Absturz: Der Linux OOM Killer
Der Arbeitsspeicher eines physischen Servers im Rechenzentrum hat eine feste Hardware-Obergrenze (z. B. 8 GB oder 16 GB). Wenn der Heap der Spiel-Engine aufgrund von Speicherlecks bis an dieses Limit anwächst, fordert die Serveranwendung zusätzliche Speicherseiten vom Betriebssystem-Kernel (meist Linux / Ubuntu) an. Der physische Knoten verfügt jedoch über keine freien RAM-Pools mehr.
Um die Stabilität des gesamten Betriebssystems zu schützen und einen globalen Hardware-Lockup zu verhindern, aktiviert der Linux-Kernel einen Schutzmechanismus: den OOM Killer (Out Of Memory Killer).
- Seine Aufgabe є es, Anwendungen mit hoher Last gewaltsam zu beenden, um einen Systemstillstand bei akutem Speichermangel zu verhindern.
- Der OOM Killer scannt die laufenden Prozesse und wählt anhand eines Badness-Scores (unter Berücksichtigung der RAM-Größe und der Laufzeit) ein Opfer aus.
- Da der Gameserver in diesem Moment fast 100 % des zugewiesenen Speichers beansprucht, greift der OOM Killer ein und sendet ein unaufhaltbares
SIGKILL-Signal (einen harten Abbruch-Befehl).
Der Server schaltet sich augenblicklich ab, ohne seine finalen Speicherstände auf die Festplatte zu schreiben, und lässt den Administrator ohne Fehlereintrag im Spiel-Log zurück.
| RAM-Zustand | Interne Serverbedingungen | Auswirkung auf das Spielerlebnis |
|---|---|---|
| Stabil (Normal) | Der Heap enthält ausschließlich aktive Objekte. Der Garbage Collector bereinigt temporäre Datenblöcke blitzschnell in einem Fenster von 1–2 ms. | Konstante Ziel-TPS (30.0 / 20.0), vollständiges Ausbleiben von Rucklern und eine stabile Latenz. |
| Leck-Akkumulation | Der Heap füllt sich mit Millionen toter Objekte, die durch fehlerhafte Referenzen blockiert sind. Der GC benötigt 50–100 ms, um den Speicherbaum zu prüfen. | Periodisches, kurzes Stottern (Stuttering), bei dem der Hauptthread temporär keine Netzwerkpakete der Clients verarbeitet. |
| Kritisch (OOM) | Der Arbeitsspeicher є zu 100 % ausgelastet. Zuweisungen für Positionsvektoren oder Inventarknoten schlagen fehl. Der OS-Kernel aktiviert den OOM Killer. | Sofortige, ungeplante Serverabschaltung. Das Hosting-Panel registriert einen plötzlichen Terminated-Status oder wirft den Error Code 137 aus. |
Das Fazit für Administratoren
Speicherlecks lassen sich nicht durch das Erhöhen der CPU-Leistung kompensieren. Auch der bloße Wechsel in einen teureren Tarif mit mehr RAM (z. B. ein Upgrade von 8 GB auf 16 GB) löst das zugrunde liegende Problem nicht; es verzögert lediglich den Absturz. Der Server fällt dann eben alle 24 Stunden aus statt alle 12.
Die einzige wirksame Verteidigung gegen progressive Speicherdegradation є eine konsequente Code-Hygiene in Ihren Addons und Skripten: Stellen Sie sicher, dass temporäre Elemente beim Verlassen eines Spielers aus globalen Arrays gelöscht werden, leeren Sie SQL-Abfragecaches und verzichten Sie auf den Einsatz veralteter, nicht mehr gepflegter Modifikationen.