In vielen Büchern und Tutorials wurde das Speichermanagement betont und ich hatte das Gefühl, dass einige mysteriöse und schreckliche Dinge passieren würden, wenn ich den Speicher nicht freigeben würde, nachdem ich ihn nicht mehr benutze.
Ich kann nicht für andere Systeme sprechen (obwohl es für mich vernünftig ist anzunehmen, dass sie eine ähnliche Vorgehensweise anwenden), aber zumindest unter Windows kann der Kernel die meisten Ressourcen (mit Ausnahme einiger weniger) bereinigen, die von verwendet werden ein Programm nach Programmende. Dazu gehört unter anderem Heap-Speicher.
Ich verstehe, warum Sie eine Datei schließen möchten, nachdem Sie sie verwendet haben, um sie dem Benutzer zur Verfügung zu stellen, oder warum Sie einen mit einem Server verbundenen Socket trennen möchten, um Bandbreite zu sparen Sie müssen ALLEN von Ihrem Programm verwendeten Speicher verwalten.
Jetzt bin ich damit einverstanden, dass diese Frage weit gefasst ist, da der Umgang mit Ihrem Speicher davon abhängt, wie viel Speicher Sie benötigen und wann Sie ihn benötigen. Daher werde ich den Umfang dieser Frage auf Folgendes eingrenzen : Wenn ich ein Stück von verwenden muss Ist es wirklich notwendig, den Speicher während der gesamten Lebensdauer meines Programms freizugeben, bevor das Programm beendet wird?
Bearbeiten: Die als Duplikat vorgeschlagene Frage war spezifisch für die Unix-Betriebssystemfamilie. Die beste Antwort war sogar ein Linux-spezifisches Tool (z. B. Valgrind). Diese Frage bezieht sich auf die meisten "normalen" nicht eingebetteten Betriebssysteme und darauf, warum es sinnvoll ist, Speicher freizugeben, der während der gesamten Lebensdauer eines Programms benötigt wird.
quelle
Antworten:
Es ist nicht obligatorisch, kann jedoch Vor- und Nachteile haben.
Wenn das Programm den Speicher während seiner Ausführungszeit einmal reserviert und ihn ansonsten erst am Ende des Prozesses freigibt, ist es möglicherweise sinnvoll, den Speicher nicht manuell freizugeben und sich auf das Betriebssystem zu verlassen. Auf jedem modernen Betriebssystem, das ich kenne, ist dies sicher. Am Ende des Prozesses wird der gesamte zugewiesene Speicher zuverlässig an das System zurückgegeben.
In einigen Fällen kann es sogar erheblich schneller sein, den zugewiesenen Speicher nicht explizit zu bereinigen als die Bereinigung.
Indem Sie jedoch den gesamten Speicher am Ende der Ausführung explizit freigeben,
Die Lebensdauer von Programmen kann sich ändern. Vielleicht ist Ihr Programm heute ein kleines Befehlszeilendienstprogramm mit einer typischen Lebensdauer von weniger als 10 Minuten, und es weist alle 10 Sekunden Speicher in Teilen von einigen KB zu - Sie müssen also vor dem Ende des Programms keinen zugewiesenen Speicher mehr freigeben. Später wird das Programm geändert und im Rahmen eines Serverprozesses mit einer Lebensdauer von mehreren Wochen erweitert. Daher ist es keine Option mehr, nicht genutzten Speicher dazwischen freizugeben, da Ihr Programm sonst mit der Zeit den gesamten verfügbaren Serverspeicher verbraucht . Dies bedeutet, dass Sie das gesamte Programm überprüfen und anschließend den Freigabecode hinzufügen müssen. Wenn Sie Glück haben, ist dies eine leichte Aufgabe. Wenn nicht, kann es so schwierig sein, dass die Chancen hoch sind, dass Sie einen Platz verpassen. Und wenn Sie in dieser Situation sind, werden Sie sich wünschen, Sie hätten das "freie" hinzugefügt
Generell gilt, dass das paarweise Schreiben von Zuweisungs- und zugehörigen Freigabecodes bei vielen Programmierern als "gute Gewohnheit" gilt: Auf diese Weise verringern Sie immer die Wahrscheinlichkeit, dass Sie den Freigabecode in Situationen vergessen, in denen der Speicher freigegeben werden muss .
quelle
main
. B. in .Das Freigeben von Speicher am Ende einer Programmausführung ist nur eine Verschwendung von CPU-Zeit. Es ist, als würde man ein Haus aufräumen, bevor man es aus dem Orbit entfernt.
Manchmal kann sich jedoch aus einem kurzfristig laufenden Programm ein Teil eines viel länger laufenden Programms entwickeln. Dann wird es notwendig, Dinge zu befreien. Wenn dies zumindest teilweise nicht berücksichtigt wurde, kann es zu erheblichen Nacharbeiten kommen.
Eine clevere Lösung hierfür ist "Talloc", mit der Sie eine Menge Speicherzuweisungen vornehmen und diese dann mit einem Aufruf wegwerfen können.
quelle
free
ist normalerweise viel schneller alsmalloc
.Sie könnten eine Sprache mit Garbage Collection verwenden (wie Scheme, Ocaml, Haskell, Common Lisp und sogar Java, Scala, Clojure).
(In den meisten GC-ed-Sprachen gibt es keine Möglichkeit , Speicher explizit und manuell freizugeben. Manchmal wurden einige Werte möglicherweise finalisiert , z. B. schlossen der GC und das Laufzeitsystem einen Datei-Handle-Wert, wenn dieser Wert nicht erreichbar war. Dies ist jedoch nicht der Fall sicher, unzuverlässig, und Sie sollten stattdessen Ihre Datei-Handles explizit schließen , da die Finalisierung niemals garantiert wird.)
Sie können für Ihr in C (oder sogar C ++) codiertes Programm auch den konservativen Müllsammler von Boehm verwenden . Sie würden dann alle Ihre
malloc
durch ersetzenGC_malloc
und sich nicht darumfree
kümmern, irgendeinen Zeiger zu verwenden. Natürlich müssen Sie die Vor- und Nachteile der Verwendung von Böhms GC verstehen. Lesen Sie auch das GC-Handbuch .Speicherverwaltung ist eine globale Eigenschaft eines Programms. In gewisser Weise ist es (und die Aktualität einiger gegebener Daten) nicht kompositionell und nicht modular, da es sich um eine ganze Programmeigenschaft handelt.
Wie bereits erwähnt,
free
empfiehlt es sich, die C-Speicherzone Ihres Heapspeichers explizit zuzuweisen. Für ein Spielzeugprogramm, das nicht viel Speicherplatz belegt, können Sie sich sogar dafür entscheiden, überhaupt keinenfree
Speicherplatz zu belegen (da nach Beendigung des Prozesses die Ressourcen einschließlich des virtuellen Adressraums vom Betriebssystem freigegeben werden).Nein, das musst du nicht. Und viele reale Programme machen sich nicht die Mühe, Speicherplatz freizugeben, der für die gesamte Lebensdauer benötigt wird (insbesondere der GCC- Compiler gibt einen Teil seines Speichers nicht frei). Wenn Sie dies jedoch tun (z. B.
free
bestimmte dynamisch zugewiesene C- Daten nicht stören ), sollten Sie dies besser kommentieren , um die Arbeit zukünftiger Programmierer für dasselbe Projekt zu vereinfachen . Ich neige dazu, zu empfehlen, dass die Menge des nicht freigegebenen Speichers begrenzt bleibt und normalerweise relativ klein ist, um den gesamten verwendeten Heap-Speicher zu belegen.Beachten Sie, dass das System
free
häufig keinen Speicher für das Betriebssystem freigibt (z. B. durch Aufrufen von munmap (2) auf POSIX-Systemen), sondern in der Regel eine Speicherzone als für die Zukunft wiederverwendbar markiertmalloc
. Insbesondere der virtuelle Adressraum (z. B./proc/self/maps
unter Linux, siehe proc (5) ....) wird möglicherweise nicht verkleinertfree
(daher geben Dienstprogramme dieselbe Menge an verwendetem Speicher für Ihren Prozess anps
odertop
melden dies).quelle
Es ist nicht notwendig , da Sie nicht versäumen werden, Ihr Programm ordnungsgemäß auszuführen, wenn Sie dies nicht tun. Es gibt jedoch Gründe, die Sie wählen können, wenn Sie eine Chance haben.
Einer der mächtigsten Fälle, auf die ich stoße (immer und immer wieder), ist, dass jemand ein kleines Stück Simulationscode schreibt, das in seiner ausführbaren Datei ausgeführt wird. Sie sagen "Wir möchten, dass dieser Code in die Simulation integriert wird." Ich frage sie dann, wie sie vorhaben, zwischen den Monte-Carlo-Läufen eine Neuinitialisierung durchzuführen, und sie schauen mich verständnislos an. "Was meinst du mit Neuinitialisierung? Sie führen das Programm nur mit neuen Einstellungen aus?"
Manchmal erleichtert eine saubere Bereinigung die Verwendung Ihrer Software erheblich . Bei vielen Beispielen wird davon ausgegangen, dass Sie niemals etwas bereinigen müssen, und Sie treffen Annahmen darüber, wie Sie mit den Daten und ihrer Lebensdauer um diese Annahmen herum umgehen können. Wenn Sie in eine neue Umgebung wechseln, in der diese Annahmen ungültig sind, funktionieren möglicherweise keine vollständigen Algorithmen mehr.
Ein Beispiel dafür, wie seltsam es werden kann, finden Sie in der Beschreibung, wie die verwalteten Sprachen mit der Finalisierung am Ende von Prozessen umgehen oder wie C # mit dem programmgesteuerten Anhalten von Anwendungsdomänen umgeht. Sie knüpfen sich aneinander, weil in diesen Extremfällen Annahmen durch die Risse fallen.
quelle
Ignorieren von Sprachen, in denen Sie Speicher nicht manuell freigeben ...
Was Sie momentan als "Programm" betrachten, könnte irgendwann zu einer Funktion oder Methode werden, die Teil eines größeren Programms ist. Und dann wird diese Funktion möglicherweise mehrmals aufgerufen. Und dann ist der Speicher, den Sie "manuell freigeben" sollten, ein Speicherverlust. Das ist natürlich ein Urteilsspruch.
quelle
Weil es sehr wahrscheinlich ist, dass Sie Ihr Programm irgendwann ändern, es in etwas anderes integrieren, mehrere Instanzen nacheinander oder parallel ausführen möchten. Dann muss dieser Speicher manuell freigegeben werden - aber Sie werden sich nicht mehr an die Umstände erinnern und es kostet Sie viel mehr Zeit, Ihr Programm neu zu verstehen.
Tun Sie Dinge, während Ihr Verständnis noch frisch ist.
Es ist eine kleine Investition, die in Zukunft hohe Renditen bringen kann.
quelle
Nein, das ist nicht nötig, aber eine gute Idee.
Sie sagen, dass Sie das Gefühl haben, dass "mysteriöse und schreckliche Dinge passieren würden, wenn ich den Speicher nicht freimachen würde, nachdem ich damit fertig bin".
Aus technischer Sicht ist die einzige Konsequenz daraus, dass Ihr Programm so lange mehr Speicher verbraucht, bis entweder eine harte Grenze erreicht ist (z. B. Ihr virtueller Adressraum erschöpft ist) oder die Leistung inakzeptabel wird. Wenn das Programm beendet werden soll, spielt dies keine Rolle, da der Prozess praktisch nicht mehr vorhanden ist. Bei den "mysteriösen und schrecklichen Dingen" geht es nur um den mentalen Zustand des Entwicklers. Das Auffinden der Ursache eines Speicherverlusts kann ein absoluter Albtraum sein (dies ist eine Untertreibung) und es erfordert viel Geschick und Disziplin, um leckfreien Code zu schreiben. Der empfohlene Ansatz zur Entwicklung dieser Fertigkeiten und Disziplinen besteht darin, immer Speicher freizugeben, wenn er nicht mehr benötigt wird, auch wenn das Programm kurz vor dem Ende steht.
Dies hat natürlich den zusätzlichen Vorteil, dass Ihr Code einfacher wiederverwendet und angepasst werden kann, wie andere bereits gesagt haben.
Allerdings gibt es mindestens einen Fall , in dem es besser ist , nicht um Speicher zu frei kurz vor Beendigung des Programms.
Stellen Sie sich den Fall vor, dass Sie Millionen kleiner Zuordnungen vorgenommen haben und diese größtenteils auf die Festplatte ausgelagert wurden. Wenn Sie anfangen, alles freizugeben, muss der größte Teil Ihres Speichers wieder in den Arbeitsspeicher übertragen werden, damit auf die Buchhaltungsinformationen zugegriffen werden kann, um die Daten sofort zu verwerfen. Dadurch kann es einige Minuten dauern, bis das Programm beendet ist! Ganz zu schweigen davon, dass in dieser Zeit viel Druck auf die Festplatte und den physischen Speicher ausgeübt wird. Wenn der physische Speicher zu Beginn knapp ist (möglicherweise wird das Programm geschlossen, weil ein anderes Programm viel Speicher verbraucht), müssen einzelne Seiten möglicherweise mehrmals ein- und ausgelagert werden, wenn mehrere Objekte aus dem Speicher freigegeben werden müssen gleiche Seite, werden aber nicht nacheinander freigegeben.
Wenn das Programm stattdessen nur abgebrochen wird, verwirft das Betriebssystem einfach den gesamten Speicher, der auf die Festplatte ausgelagert wurde. Dies erfolgt fast augenblicklich, da überhaupt kein Zugriff auf die Festplatte erforderlich ist.
Es ist wichtig zu beachten, dass in einer OO-Sprache der Aufruf des Destruktors eines Objekts auch das Austauschen des Speichers erzwingt. Wenn Sie dies tun müssen, können Sie auch den Speicher freigeben.
quelle
Die Hauptgründe für die manuelle Bereinigung sind: Es ist weniger wahrscheinlich, dass Sie ein echtes Speicherverlust für einen Speicherblock halten, der nur beim Beenden bereinigt wird. Manchmal treten nur dann Fehler im Allokator auf, wenn Sie die gesamte Datenstruktur durchsuchen ausplanen es, und Sie werden einen viel kleineren Kopfschmerzen haben , wenn Sie jemals so umgestalten , dass ein Objekt nicht vor dem Programm beendet erhalten ausgeplant müssen.
Die Hauptgründe, warum Sie nicht manuell bereinigen sollten, sind: Leistung, Leistung, Leistung und die Möglichkeit, dass Ihr unnötiger Bereinigungscode einen Fehler enthält, der sich in einen Absturzfehler oder eine Sicherheitslücke verwandeln kann Ausbeuten.
Wenn Sie Wert auf Leistung legen, möchten Sie immer ein Profil erstellen, um herauszufinden, wo Sie Ihre ganze Zeit verschwenden, anstatt zu raten. Ein möglicher Kompromiss besteht darin, Ihren optionalen Bereinigungscode in einen bedingten Block zu packen, ihn beim Debuggen zu belassen, damit Sie die Vorteile des Schreibens und Debuggens des Codes nutzen können, und dann, wenn Sie empirisch festgestellt haben, dass es zu viel ist Weisen Sie den Compiler an, es in Ihrer endgültigen ausführbaren Datei zu überspringen. Und eine Verzögerung beim Abschluss des Programms ist per definitionem kaum auf dem kritischen Pfad.
quelle