Manuelle Speicherverwaltung - Manual memory management

In der Informatik , manuelle Speicherverwaltung bezieht sich auf die Verwendung der Anleitung durch den Programmierer nicht genutzte Objekte zu identifizieren und freigeben oder Müll . Bis Mitte der 1990er Jahre unterstützten die meisten in der Industrie verwendeten Programmiersprachen die manuelle Speicherverwaltung, obwohl die Müllsammlung seit 1959 existiert, als sie mit Lisp eingeführt wurde . Heutzutage werden jedoch Sprachen mit Garbage Collection wie Java immer beliebter und die Sprachen Objective-C und Swift bieten eine ähnliche Funktionalität durch Automatic Reference Counting . Die wichtigsten manuell verwalteten Sprachen, die heute noch weit verbreitet sind, sind C und C++ – siehe C dynamische Speicherzuweisung .

Beschreibung

Alle Programmiersprachen verwenden manuelle Techniken, um zu bestimmen, wann ein neues Objekt aus dem freien Speicher zugewiesen werden soll . C verwendet die mallocFunktion; C++ und Java verwenden den newOperator; und viele andere Sprachen (wie Python) weisen alle Objekte aus dem kostenlosen Speicher zu. Die Bestimmung, wann ein Objekt erstellt werden soll ( Objekterstellung ), ist im Allgemeinen trivial und unproblematisch, obwohl Techniken wie Objektpools bedeuten, dass ein Objekt vor der sofortigen Verwendung erstellt werden kann. Die eigentliche Herausforderung ist die Objektzerstörung – die Feststellung, wann ein Objekt nicht mehr benötigt wird (dh es ist Müll) und dafür sorgen, dass der zugrunde liegende Speicher zur Wiederverwendung an den freien Speicher zurückgegeben wird. Bei manueller Speicherzuweisung wird dies ebenfalls manuell vom Programmierer vorgegeben; über Funktionen wie free()in C oder den deleteOperator in C++ – dies steht im Gegensatz zur automatischen Zerstörung von Objekten, die in automatischen Variablen enthalten sind , insbesondere (nicht statische) lokale Variablen von Funktionen, die in C und C++ am Ende ihres Geltungsbereichs zerstört werden.

Manuelle Speicherverwaltungstechniken

Zum Beispiel


Manuelle Verwaltung und Korrektheit

Es ist bekannt, dass die manuelle Speicherverwaltung bei falscher Verwendung mehrere Hauptklassen von Fehlern in einem Programm aktiviert, insbesondere Verletzungen der Speichersicherheit oder Speicherlecks . Diese sind eine bedeutende Quelle für Sicherheitslücken .

  • Wenn ein ungenutztes Objekt nie wieder in den freien Speicher freigegeben wird, wird dies als Speicherverlust bezeichnet . In einigen Fällen können Speicherlecks tolerierbar sein, wie beispielsweise ein Programm, das eine begrenzte Speichermenge während seiner Lebensdauer "leckt", oder ein Programm mit kurzer Laufzeit, das sich darauf verlässt, dass ein Betriebssystem seine Ressourcen freigibt, wenn es beendet wird. In vielen Fällen treten jedoch Speicherverluste in Programmen mit langer Laufzeit auf, und in solchen Fällen wird eine unbegrenzte Speichermenge verloren. In diesem Fall nimmt die Größe des verfügbaren kostenlosen Speichers im Laufe der Zeit weiter ab. wenn es endlich erschöpft ist, stürzt das Programm dann ab.
  • Ein katastrophaler Ausfall des dynamischen Speicherverwaltungssystems kann die Folge sein, wenn der Sicherungsspeicher eines Objekts mehr als einmal darunter gelöscht wird; ein Objekt wird explizit mehr als einmal zerstört; wenn ein Programmierer während der Verwendung eines Zeigers zum Manipulieren eines nicht im freien Speicher zugewiesenen Objekts versucht, den Sicherungsspeicher des Zielobjekts des Zeigers freizugeben; oder wenn ein Programmierer beim Manipulieren eines Objekts über einen Zeiger auf einen anderen, willkürlichen Speicherbereich, der von einer unbekannten externen Task, einem Thread oder Prozess verwaltet wird, den Zustand dieses Objekts korrumpiert, möglicherweise so, dass er außerhalb seiner Grenzen schreibt und korrumpiert seine Speicherverwaltungsdaten. Das Ergebnis solcher Aktionen kann Heap-Beschädigung , vorzeitige Zerstörung eines anderen (und neu erstellten) Objekts sein, das zufällig dieselbe Stelle im Speicher einnimmt wie das mehrfach gelöschte Objekt, Programmabstürze aufgrund eines Segmentierungsfehlers (Verletzung des Speicherschutzes ) und andere Formen von undefiniertem Verhalten .
  • Zeiger auf gelöschte Objekte werden zu wilden Zeigern, wenn sie nach dem Löschen verwendet werden; Der Versuch, solche Zeiger zu verwenden, kann zu schwer zu diagnostizierenden Fehlern führen.

Es ist bekannt, dass Sprachen, die ausschließlich Garbage Collection verwenden , die letzten beiden Fehlerklassen vermeiden. Speicherverluste können immer noch auftreten (und begrenzte Lecks treten häufig bei generationsübergreifender oder konservativer Garbage Collection auf), sind jedoch im Allgemeinen weniger schwerwiegend als Speicherverluste in manuellen Systemen.

Ressourcenbeschaffung ist Initialisierung

Die manuelle Speicherverwaltung hat einen Korrektheitsvorteil, nämlich dass sie eine automatische Ressourcenverwaltung über das Paradigma Resource Acquisition Is Initialization (RAII) ermöglicht.

Dies tritt auf, wenn Objekte knappe Systemressourcen besitzen (wie Grafikressourcen, Datei-Handles oder Datenbankverbindungen), die aufgegeben werden müssen, wenn ein Objekt zerstört wird – wenn die Lebensdauer des Ressourcenbesitzes an die Lebensdauer des Objekts gebunden werden soll. Sprachen mit manueller Verwaltung können dies arrangieren, indem sie die Ressource während der Objektinitialisierung (im Konstruktor) erwerben und während der Objektzerstörung (im Destruktor ) freigeben , die zu einem bestimmten Zeitpunkt erfolgt. Dies wird als Ressourcenbeschaffung ist Initialisierung bezeichnet.

Dies kann auch bei deterministischer Referenzzählung verwendet werden . In C++ wird diese Fähigkeit weiter genutzt, um die Speicherfreigabe innerhalb eines ansonsten manuellen Rahmens zu automatisieren. Die Verwendung der shared_ptrVorlage in der Standardbibliothek der Sprache zur Durchführung der Speicherverwaltung ist ein gängiges Paradigma. shared_ptrist jedoch nicht für alle Objektnutzungsmuster geeignet.

Dieser Ansatz ist in den meisten Garbage-Collection-Sprachen – insbesondere beim Tracing von Garbage Collectors oder fortgeschritteneren Referenzzählungen – nicht anwendbar, da die Finalisierung nicht deterministisch ist und manchmal überhaupt nicht stattfindet. Das heißt, es ist schwierig zu definieren (oder zu bestimmen), wann oder ob eine Finalizer- Methode aufgerufen werden könnte; Dies wird allgemein als Finalizer-Problem bezeichnet . Java und andere GC-Sprachen verwenden häufig eine manuelle Verwaltung für knappe Systemressourcen neben dem Speicher über das Dispose-Muster : Von jedem Objekt, das Ressourcen verwaltet, wird erwartet, dass es die dispose()Methode implementiert , die solche Ressourcen freigibt und das Objekt als inaktiv markiert. Von Programmierern wird erwartet, dass sie dispose()manuell aufrufen, wenn dies angemessen ist, um ein "Auslaufen" knapper Grafikressourcen zu verhindern. Abhängig von der finalize()Methode (wie Java Finalizer implementiert) zur Freigabe von Grafikressourcen wird von Java-Programmierern weithin als schlechte Programmierpraxis angesehen, und ähnlich __del__()kann man sich nicht auf die analoge Methode in Python für die Freigabe von Ressourcen verlassen. Für Stack-Ressourcen (Ressourcen, die innerhalb eines einzigen Codeblocks erworben und freigegeben werden) kann dies durch verschiedene Sprachkonstrukte wie Pythons with, C#s usingoder Javas try-with-resources automatisiert werden .

Leistung

Viele Befürworter der manuellen Speicherverwaltung argumentieren, dass sie im Vergleich zu automatischen Techniken wie der Garbage Collection eine bessere Leistung bietet . Traditionell war die Latenz der größte Vorteil, aber das ist nicht mehr der Fall. Die manuelle Zuweisung hat häufig eine überlegene Referenzlokalität .

Es ist auch bekannt, dass die manuelle Zuweisung aufgrund der schnelleren Wiederherstellung für Systeme geeigneter ist, bei denen Speicher eine knappe Ressource ist. Speichersysteme können und tun es häufig, wenn sich die Größe des Arbeitssatzes eines Programms der Größe des verfügbaren Speichers annähert; Nicht verwendete Objekte in einem Garbage-Collection-System bleiben länger als in manuell verwalteten Systemen in einem nicht freigegebenen Zustand, da sie nicht sofort freigegeben werden, wodurch die effektive Workingset-Größe erhöht wird.

Manuelle Verwaltung hat eine Reihe von dokumentierten Leistung Nachteile :

  • Aufrufe an deleteund dergleichen verursachen jedes Mal, wenn sie getätigt werden, einen Overhead, dieser Overhead kann in Garbage-Collection-Zyklen amortisiert werden. Dies gilt insbesondere für Multithread-Anwendungen, bei denen Löschaufrufe synchronisiert werden müssen.
  • Die Zuweisungsroutine kann komplizierter und langsamer sein. Einige Garbage-Collection-Schemata, z. B. solche mit Heap- Komprimierung, können den freien Speicher als einfaches Speicherarray beibehalten (im Gegensatz zu den komplizierten Implementierungen, die bei manuellen Verwaltungsschemata erforderlich sind).

Latenz ist ein umstrittener Punkt, der sich im Laufe der Zeit geändert hat, wobei frühe Garbage Collectors und einfache Implementierungen im Vergleich zur manuellen Speicherverwaltung sehr schlecht abschneiden, aber anspruchsvolle moderne Garbage Collectors oft genauso gut oder besser arbeiten als die manuelle Speicherverwaltung.

Die manuelle Zuweisung leidet nicht unter den langen "Pause"-Zeiten, die bei der einfachen Stop-the-World-Müllsammlung auftreten, obwohl moderne Müllsammler Sammelzyklen haben, die oft nicht wahrnehmbar sind.

Sowohl die manuelle Speicherverwaltung als auch die Garbage Collection leiden unter potenziell unbegrenzten Aufhebungszeiten – die manuelle Speicherverwaltung, da die Aufhebung der Zuweisung eines einzelnen Objekts die Aufhebung der Zuweisung seiner Member und rekursiv der Member seiner Member usw. erfordern kann, während die Garbage Collection lange Auflistungszyklen haben kann. Dies ist insbesondere bei Echtzeitsystemen ein Problem , bei denen unbegrenzte Erfassungszyklen im Allgemeinen nicht akzeptabel sind; eine Garbage Collection in Echtzeit ist möglich, indem die Garbage Collection angehalten wird, während die manuelle Speicherverwaltung in Echtzeit das Vermeiden großer Freigaben oder das manuelle Anhalten der Freigabe erfordert.

Verweise

  • Berger, ED; Zorn, BG; McKinley, KS (November 2002). "Überdenken der benutzerdefinierten Speicherzuweisung". Tagungsband der 17. ACM SIGPLAN Konferenz zu Objektorientierte Programmierung, Systeme, Sprachen und Anwendungen . OOPSLA '02. S. 1–12. CiteSeerX  10.1.1.119.5298 . doi : 10.1145/582419.582421 . ISBN 1-58113-471-1.

Externe Links