Die Frage taucht immer häufiger auf und die Meinungen gehen da weit auseinander. Es scheint ein einfaches Thema zu sein und irgendwie will da jeder mitreden. Aber ganz so einfach ist es nicht…
Wenn der Hauptspeicher ausging - es ist eine begrenzte Ressource - dann besteht offensichtlich ein Problem. Die Lösung war, das ganze Prozesse auf externe Speicher wie Magnetbänder oder Festplatten ausgelagert wurden. Das ist eine sehr langsame Lösung, denn es muss auch alles zum Abarbeiten wieder eingelesen werden.
Heutzutage passiert das nicht mehr, es werden nur einzlene Teilbereiche ausgelagert, sogenannte pages, das sind in aller Regel Blöcke von 4 kB Größe. Dadurch bleibt das meiste im Hauptspeicher, so weit es geht.
Welche Seiten ausgelagert werden, ist eine wichtige Frage. In Frage kommen hier vor allem Bereiche, die nie oder nur sehr selten benutzt werden. Deren fehlen fällt also gar nicht oder nur kaum auf. Eine Herausforderung besteht aber darin, diese zu finden. Dafür sorgt der Kenerldienst kswapd. Dieser lagert wenig oder gar nicht genutzte, insbesondere schon seit langem nicht mehr genutzte Seite aus, sollte der freie Speicher knapp werden.
Wie aggressiv das passiert, kann über den Kernelparamter vm.swappiness
gesteuert werden. Je höher der Weter (0-100), desto aggressiver werden
Seiten ausgelagert.
Das ist einer der häufigsten Sätze, die man zu diesem Thema hört. Swap ist langsam, das will ich nicht, ich habe genug RAM, ich brauche keinen Swapspace.
Und damit ist das Thema doch schon erledigt, oder?
Auf dem Desktop scheint das durchaus eine gangbare Lösung zu sein, viele scheinen damit auch gut zu fahren. Da wird dann doch ein Swapbereich angelegt, da man so etwas wie suspend-to-disk nutzen will: Die Auslagerung von allem um nach einem Neustart an der Stelle weiterarbeiten zu können wo man aufgehört hat.
Gleichzeitig wir dann vm.swappiness=0
gesetzt, um das Paging zu
verhindern.
Aber selbst wenn das scheinbar propblemlos läuft, sollte man es auf einem Server auch so betreiben?
Man sollte einmal einen Blick darauf werfen, wozu der Hauptspeicher genutzt wird:
Die ersten beiden Punkte sind offensichtlich. Die zwei anderen sind aber auch von Interessant. Daten, die zum Beispiel auf eine Festplatte geschrieben werden sollen, werden nicht sofort geschrieben, sie landen erst in einem Datenpuffer.
Der Grund ist einfach: Das Programm müsste so lange angehalten werden, bis alle Daten geschrieben sind. Solche Fälle gibt es auch, aber in aller Regel ist es einem Programm egal, ob die Daten auch schon wirklich geschrieben wurden, es arbeitet dann einfach weiter.
Die Daten aus den Datenpuffer (buffer) werden dann vom Kernel im Hintergrund auf den Datenträger geschrieben. Das erhöht die Ablauf- geschwindigkeit der Programme immens.
Was ist aber mit Daten, die man geschrieben hat und wieder einlesen möchte? Hier muss das Programm dann so lange warten, bis die Daten vom externen Speicher eingelesen wurden. Das ist meistens nicht sehr performant.
Wie wäre es aber, wenn man den freien Speicher dazu verwendet, die Daten, die vorher im Datenpuffer waren, dort zu belassen damit man sie bei Bedarf wieder schnell einlesen, also aus dem RAM holen, kann?
Das ist die Aufgabe vom Datencache: Warum sollte man den freien Speicher nicht dafür verwenden? Das Gute daran ist auch, dass man diesen Speicher jederzeit problemlos freigeben kann.
Wenn ein Datenpuffer und -cache im RAM sehr effektiv sind, ungenutzte Seiten jedoch vermutlich nie oder nur sehr selten benutzt werden, wie geht man damit um, wenn der Hauptspeicher sich füllt?
Das ist nun der Punkt, wo vm.swappiness
eine Rolle spielt. Sollte
man nicht die vermeintlich nie bis selten genutzen Seiten auslagern
um die Vorteile von Datencache und Datenpuffer besser nutzen zu können?
Schließlich profitieren die Programme immens von diesen zwei Funktionen,
es wäre dann doch schlecht, sie nicht zu nutzen. Es ist also durchaus
sinnvoll, dass die vm.swappiness
nicht auf Null gesetzt werden sollte
und man einen Swapbereich hat.
Warum sollte man teile vom Programmcode in den Swap schreiben, das Programm liegt doch schon auf der Festplatte. Genau, die werden auch gar nicht in den Swap ausgelagert…
Aber was ist mit dem vielen Speicherbedarf? Ein Programm legt in der Regel beim Start oder später bei der weiteren Ausführung fest, wieviel Speicher es denkt zu benötigen. Das wird dem Kernel mitgeteil und er reserviert dafür Speicherplatz. Was ist aber, wenn das Programm den Speicher doch nicht im vollen Umfang benötigt?
Viele Programme greifen auf bestehende Bibliotheken zu, diese allokieren oft reichlich Speicherplatz, egal ob sie ihn benötigen oder auch nicht. In aller Regel ist der Speicher auch vorhanden, der Progammierer denkt dabei gar nicht darüber nach.
Wenn die Programme aber viel Speicher verlangen, ihn jedoch nicht benötigen, dann könnte man ihnen den doch einfach nur scheinbar geben.
So macht es der Kernel in aller Regel auch, es gibt dann die zwei Teile RSS, den sogenannten Resident Set Size und den VSZ, die sogenannte Virtual memory SiZe. Erstere muss wirklich im RAM liegen, für letzteres reicht Speicherplatz im Swap aus. Dieser wird erst bereitgestellt, wenn er wirklich benötigt wird.
Das klingt doch gut.
Nur: Wenn die Programme doch alle deutlich mehr Speicher anfordern, als sie benötigen, muss man deswegen gleich so großen Swapspace bereithalten?
Die Antwort ist nicht ganz so einfach.
Bei Linux wurde aus diesem Grund das vm.overcommit_memory
eingeführt.
Damit kann man der Kernel angewiesen werden, jede
Speicherplatzanforderung einfach zu bewilligen. Damit entfällt auch die
Notwendigkeit diesen Speicherplatz überhaupt irgendwo zu haben.
Die Idee ist, dass die Programme wirklich zuviel Speicher anfordern, den sie in Wircklichkeit nie nutzen. Damit ist das Swap-Problem gelöst, man braucht ihn nicht mehr.
Aber: Was ist, wenn die Programme den Speicher doch benötigen?
Das ist ein sehr großes Problem: Wenn ein Programm Speicher anfordert, so bekommt es normalerweise als Antwort den Speicher zugeteilt oder die Fehlermeldung, dass kein Speicher mehr vorhanden ist. Darauf kann das Programm reagieren, eine Fehlermeldung generieren, mit weniger Speicher arbeiten, anderweitig welchen freigeben oder gar warten, bis die Anforderung erfüllbar ist.
Wenn aber der Speicher zugesagt wurde, er aber dann doch nicht da ist, wird das Programm mit SIGSEGV abgebrochen. Das ist fatal, es kann zwar das Signal abfangen und dann umschiffen, es fehlt jedoch der Kontext um geeignet damit umzugehen.
Bei einem Desktop-System ist das vermutlich egal, das Programm wird einfach neu gestartet und wenn das noch immer nicht funktioniert, wird das gesamte System einfach neu gestartet. Dann ist wieder reichlich freier Speicherplatz vorhanden.
Aber was ist mit einem Server? Hier wird doch erwartet, dass dieser am Besten das ganze Jahr ohne Unterbrechung durchläuft. Da wäre doch so eine Einstellung mehr als fatal!
Wenn man aber das vm.overcommit_memory
hier abstellt, dann wird auch
ein entsprechend großer Swapspeicher benötigt. Andernfalls könnte ein
Programm gar keinen Speicher bekommen.
Einfach ausgedrückt, ist RSS der Speicher, der im RAM liegt, VSZ der, der (virtuell) im Swap liegt.
Das ist nicht ganz korrekt, wenn man alle RSS-Werte aufsummiert, kann es mehr sein, als Hauptspeicher im System ist. Das liegt aber an einem anderen Konzept: shared memory und shared libraries Dise Speicherteile sind in mehreren Programmen identisch vorhanden und werden nur einmal im Speicher gehalten, jedoch bei jedem Prozess als ihm gehörig aufgelistet.
Aber als Faustregel kann man sagen: RSS+VSZ von allen Prozessen sollte immer kleiner sein als RAM+Swap.
Was passiert nun, wenn wir kein overcommit vom Speicher zulassen und ein RSS+VSZ ausgeschöpft sind? Dann müsste das System stehen bleiben, es hat keinen Handlungsspielraum mehr, es würde einfrieren. Manche Betriebssyteme machen das, Linux hat da eine andere Lösung: OOM-Killer. Das ist ein Programm, dass laufende Prozesse beendet um Speicher freizubekommen. Das ist nicht ideal, mitunter wichtige Dienste laufen dann nicht mehr. Dafür ist das Gesamtsystem aber noch handlungsfähig.
Damit wären wir wieder bei der Eingangsfrage: Ich denke, zumindest auf
einem Server sollte man nicht ohne Swapspeicher arbeiten und vor allem
das vm.overcommit_memory=2
setzen. Alles andere ist sonst ein
Glücksspiel.
Die Größe des Swaps ist dann in der Tat eine heikle Frage und ich denke, bei einem Server sollte der sehr wohl recht groß sein, auch wenn er vermutlich nie wirklich genutzt wird. Aber wer viel Geld für RAM ausgibt, sollte auch noch genug Geld haben, um auch einen brauchbaren Swapbereich finanzieren zu können.
Auch so Effekte wie Datencache und Datenpuffer sind sehr wichtig, von daher sollte man nicht beunruhigt sein, wenn ein paar Gigabyte Swap belegt sind, hier ist ein Beispiel:
$ free -h
total used free shared buff/cache available
Mem: 1.5Ti 489Gi 993Gi 110Mi 27Gi 1.0Ti
Swap: 5.4Ti 322Gi 5.0Ti
Das System arbeitet dabei tadellos, es sind keine Beeinträchtigungen zu bemerken.
Das ist nun der schlimmste Fall der eintreten kann, daher wollen viele diesen dadurch vermeiden, dass sie gar keinen Swapbereich anlegen. Es müssen dann Seiten aus- und eingelesen werden, die aktive genutzt werden. Das Programm hält dann so lange an, bis die Seite da ist. Bei sehr aktiven Programmen geht dann der CPU-Anteil von 100% auf unter 1% hinunter! Das sollte mit allen Mitteln vermieden werden.
Das ist eine schwierige Frage: Sicherlich ist das ein- und auslagern von Seiten davon abhängig, wie schnell das Medium ist. Bei klassischen Festplatten muss der Lesekopf erst an die richtige Stelle bewegt werden, die Daten müssen eingelesen werden, wenn sie am Lesekopf vorbeikommen.
Da ist sicherlich eine SSD deutlich besser. Aber wenn man nun glaubt, eine NVMe-SSD ist fast so schnell wie RAM, damit dürfte es doch keine Einschränkung geben, der sollte sich vielleicht nicht wundern, wenn es dennoch langsam ist.
Die hohe Geschwindigkeit wird gewöhnlich über mehrere parallele Datenströme erreicht, beim pagen ist es aber in aller Regel immer nur ein Block. Auch Effekt wie Datencache und Datenpuffer sind hier nicht hilfreich, der Speicher ist schon ausgegangen. Das Einlesen der Datenseite um sie dann auszführen, benötigt auch seine Zeit.
Ich denke, jeder der einene Server betreibt, ist gut darin beraten, auch einen Swapbereich anzulegen und das overcommit des Speichers abzuschalten. Wenn letzteres aber abgeschaltet ist, muss im freien Bereich auch genug Speicher für die Programme vorhanden sein, also auch im erst einmal nicht-genutzten virtuellen Bereich VSZ. Wenn dieser fehlt, brechen die meisten Programme mit no memory available ab.
Das ist jetzt nicht alles zu 100% präzise und mitunter ändern sich auch Dinge. Aber dennoch denke ich, dass ich damit nah an dem dran bin, was sinnvoll ist und was nicht.
Klar, wer ein Notebook mit 16 GB RAM und 128 GB SSD hat, der will nicht noch 16 GB oder mehr für Swap opfern, vor allem wenn es meistens völlig ungenutzt aussieht. Aber ein Notebook wird auch häufiger einmal neu gestartet und es ist auch nicht dramatisch, wenngleich unschön, wenn ein Programm einmal abstürzt.