Warum Ihre App den Heap mehr als den Stack benötigt

8

Ihr Computer verfügt nicht nur über RAM. Es gibt eine chaotische, fragmentierte, sich ständig verändernde Speicherlandschaft, die die CPU vorgibt, unendlich zu sein. Dies ist die Realität dynamischer Datenstrukturen, die auf moderner Hardware ausgeführt werden.

Eine typische Workstation verfügt heute über 16 bis 64 Megabyte physischen RAM. Aber lassen Sie sich von diesen kleinen Zahlen nicht täuschen. Durch eine Technik namens virtueller Speicher tauscht das System Daten zwischen diesem RAM und der Festplatte aus. Die CPU sieht eine Illusion. Es geht davon aus, dass es 200 bis 500 Megabyte zusammenhängenden Speicherplatz hat. Die Illusion funktioniert. Der Code wird ausgeführt. Wenn das Betriebssystem jedoch auf die Festplatte zugreifen muss, um eine Seite Speicher abzurufen, wird alles langsamer. Man spürt es in der Verzögerung. Man spürt es im Fan-Spin-Up. Trotz der Leistungseinbußen ist virtueller Speicher eine kostengünstige Möglichkeit, Ihren Arbeitsspeicher zu „erweitern“. Es ist ein notwendiger Kompromiss.

Gehen wir von einer sauberen Weste aus. Ein Gesamtspeicherplatz von 50 Megabyte. Nicht mehr. Nicht weniger. Das ist das Budget.

Das Betriebssystem besitzt diesen 50-Megabyte-Block. Es teilt den Kuchen. Nicht gleichermaßen. Nie gleich.

Da ist zunächst der Code. Die ausführbaren Anweisungen für jede derzeit ausgeführte Anwendung sowie den Betriebssystemkernel selbst. Dieser Abschnitt ist statisch. Es ändert sich nicht, während das Programm aktiv ist. Als nächstes kommen die globalen Variablen. Dies sind die gemeinsam genutzten Konstanten und Zustandsflags, die jeder Teil der Anwendung berühren kann. Sie bleiben vom Start der App bis zu ihrem Tod im Gedächtnis.

Dann ist da noch der Stapel.

Der Stapel ist starr. Es wächst und schrumpft im Gleichschritt mit Funktionsaufrufen. Wenn Sie eine Funktion aufrufen, werden ihre lokalen Variablen und Parameter auf den Stapel verschoben. Wenn die Funktion zurückkehrt, werden sie entfernt. Der Stack merkt sich die Aufrufreihenfolge. Es stellt sicher, dass Sie an den richtigen Ort zurückkehren. Aber es hat eine maximale Größe. Sie können den Stack während des Flugs nicht um mehr Speicher bitten. Wenn Sie es versuchen, läuft der Stapel über. Das Programm stürzt ab.

Wenn ein Programm beendet ist, wird es vom Betriebssystem entladen. Der Code, die Globals, der Stapelspeicher – alles wird gelöscht. Diese Erinnerung wird recycelt. Es ist bereit für das nächste Programm.

Aber hier liegt das Problem. Zu jedem Zeitpunkt könnten etwa 50 Prozent dieses 50-Megabyte-Speicherplatzes ungenutzt sein. Warum? Denn der Stack enthält nur das, was gerade ausgeführt wird. Der Code enthält nur die Anweisungen. Die ungenutzten Speicherblöcke sind verstreut. Es sind Löcher.

Das Betriebssystem nimmt diese Lücken und gruppiert sie. Dieser kollektive Pool wird der Heap genannt.

Hier erfolgt die dynamische Zuordnung. Der Heap ist der einzige Ort im Speicher, an dem ein Programm genau dann eine genaue Menge an Speicherplatz anfordern kann, wenn es ihn benötigt. Sie deklarieren zur Kompilierungszeit kein Array mit 1000 Ganzzahlen. Sie wissen nicht, ob Sie 1.000 oder 100.000 benötigen. Du wartest. Sie führen den Code aus. Sie entscheiden, dass Sie diesen Platz brauchen. Dann rufen Sie „malloc“ auf.

„malloc“ steht für „Memory Allocate“. Es greift einen Block vom Heap. Es gibt einen Zeiger zurück. Sie verwenden diesen Zeiger. Wenn Sie fertig sind, rufen Sie „Frei“ an. Der Block geht zurück in den Pool.

Dies ist der grundlegende Unterschied zwischen statischem und dynamischem Speicher. Der Stapel erfolgt automatisch. Es ist an den Umfang gebunden. Der Heap ist manuell. Es ist an Absicht gebunden.

Warum ist Ihnen das wichtig? Denn die meisten Fehler in komplexer Software sind nicht auf schlechte Logik zurückzuführen. Sie sind auf eine schlechte Speicherverwaltung zurückzuführen. Wenn Sie vom Heap zuweisen und vergessen, ihn freizugeben, geht Speicher verloren. Der Haufen schrumpft. Irgendwann geht dem System der Speicherplatz aus. Programme frieren ein. Das Betriebssystem beendet Prozesse. Sie verlieren nicht gespeicherte Arbeit.

Wenn Sie umgekehrt versuchen, zu viel auf dem Stapel zuzuweisen, sprengen Sie das Stapellimit. Das Programm wird abrupt beendet.

Der Heap ermöglicht Flexibilität. Es ermöglicht wachsende Datenstrukturen. Verknüpfte Listen. Bäume. Grafiken. Keines davon kann zur Kompilierzeit mit einer festen Größe definiert werden. Sie müssen im Handumdrehen erstellt werden. Sie müssen im Haufen leben.

Ist der Haufen perfekt? Nein. Es fragmentiert. Wenn Sie Blöcke unterschiedlicher Größe zuweisen und freigeben, wird der Haufen zu einem Puzzle mit fehlenden Teilen. Das Auffinden eines zusammenhängenden Blocks freien Speichers kann langsam sein. Aus diesem Grund kann „malloc“ manchmal merklich viel Zeit in Anspruch nehmen. Es muss gesucht werden. Es muss zusammenwachsen. Es muss das Chaos bewältigen.

Aber ohne den Heap bricht die moderne Datenverarbeitung zusammen.