Wird beim Beenden einer C-Anwendung der malloced Speicher automatisch freigegeben?

90

Angenommen, ich habe den folgenden C-Code:

int main () {
  int *p = malloc(10 * sizeof *p);
  *p = 42;
  return 0;  //Exiting without freeing the allocated memory
}

Wenn ich dieses C-Programm kompiliere und ausführe, dh nachdem ich Speicherplatz zugewiesen habe, wird der von mir zugewiesene Speicher nach dem Beenden der Anwendung und dem Beenden des Prozesses weiterhin zugewiesen (dh im Grunde genommen Speicherplatz beanspruchend)?

Andreas Grech
quelle
9
Es ist ein "guter Stil", Ihren Speicher zu bereinigen, nicht weil Sie möglicherweise auf einem Betriebssystem ohne geschützten Speicher ausgeführt werden (was der Hauptvorschlag unten ist), sondern weil es die Wahrscheinlichkeit erhöht, dass Speicherlecks auftreten und erhalten bleiben Ihr Code schlank und korrekt ...
Matt Joiner

Antworten:

110

Dies hängt vom Betriebssystem ab. Die Mehrheit der modernen (und aller wichtigen) Betriebssysteme gibt Speicher frei, der vom Programm nicht freigegeben wird, wenn es endet.

Sich darauf zu verlassen ist eine schlechte Praxis und es ist besser, sie explizit freizugeben. Das Problem ist nicht nur, dass Ihr Code schlecht aussieht. Möglicherweise möchten Sie Ihr kleines Programm in ein größeres, lang laufendes Programm integrieren. Dann, eine Weile später, müssen Sie Stunden damit verbringen, Speicherlecks aufzuspüren.
Wenn Sie sich auf eine Funktion eines Betriebssystems verlassen, ist der Code auch weniger portabel.

Yacoby
quelle
16
Ich bin einmal auf einer eingebetteten Plattform auf win98 gestoßen, und aufgrund dieser Erfahrung kann ich sagen, dass beim Schließen von Programmen KEIN Speicher frei wird.
San Jacinto
8
@ Ken Es war ein Beispiel. Es gibt auch eine Linie zwischen YAGNI und schlampiger Codierung. Wenn Ressourcen nicht freigegeben werden, wird dies überschritten. Das YAGNI-Prinzip sollte auch auf Funktionen angewendet werden, nicht auf Code, mit dem das Programm ordnungsgemäß funktioniert. (Und Speicher nicht freizugeben ist ein Fehler).
Yacoby
5
+1: Das Wichtigste ist, dass die Speicherverwaltung so ist, wie Yacoby ganz richtig sagt: "eine Funktion des Betriebssystems" . Sofern ich mich nicht irre, definiert die Programmiersprache nicht, was vor oder nach der Programmausführung passiert.
D. Shawley
9
Das manuelle Freigeben von Speicher nimmt mehr Zeit in Anspruch, benötigt mehr Code und führt zu möglichen Fehlern (sagen Sie mir, dass Sie noch nie einen Fehler im Freigabecode gesehen haben!). Es ist nicht "schlampig", absichtlich etwas wegzulassen, das für Ihren speziellen Anwendungsfall in jeder Hinsicht schlechter ist. Sofern oder bis Sie beabsichtigen, es auf einem alten / winzigen System auszuführen, das nach Beendigung des Prozesses keine Seiten freigeben oder in ein größeres Programm (YAGNI) integrieren kann, sieht es für mich nach einem Nettoverlust aus. Ich weiß, es tut dem Ego eines Programmierers weh, daran zu denken, es nicht selbst aufzuräumen, aber auf welche praktische Weise ist es tatsächlich besser?
Ken
6
Jeder, der Speicherlecks auf SO vorschlägt, sollte von allen Reputationen und Abzeichen befreit werden
Ulterior
51

Im Allgemeinen bereinigen moderne Allzweckbetriebssysteme nach Beendigung von Prozessen . Dies ist erforderlich, da die Alternative darin besteht, dass das System im Laufe der Zeit Ressourcen verliert und einen Neustart aufgrund von Programmen erfordert, die schlecht geschrieben sind oder einfach selten auftretende Fehler aufweisen, durch die Ressourcen verloren gehen.

Es kann aus verschiedenen Gründen eine gute Praxis sein, wenn Ihr Programm seine Ressourcen ohnehin explizit freigibt, z. B.:

  • Wenn Sie über zusätzliche Ressourcen verfügen, die beim Beenden nicht vom Betriebssystem bereinigt werden, z. B. temporäre Dateien oder Änderungen des Status einer externen Ressource, benötigen Sie Code, um all diese Dinge beim Beenden zu erledigen wird oft elegant mit der Befreiung des Gedächtnisses kombiniert.
  • Wenn Ihr Programm eine längere Lebensdauer hat, möchten Sie nicht, dass der einzige Weg, um Speicher freizugeben, das Beenden ist. Beispielsweise möchten Sie Ihr Programm möglicherweise in einen Server (Daemon) konvertieren, der weiterhin ausgeführt wird, während viele Anforderungen für einzelne Arbeitseinheiten bearbeitet werden, oder Ihr Programm wird möglicherweise zu einem kleinen Teil eines größeren Programms.

Hier ist jedoch ein Grund, das Freigeben von Speicher zu überspringen: effizientes Herunterfahren . Angenommen, Ihre Anwendung enthält einen großen Cache im Speicher. Wenn es beim Beenden die gesamte Cache-Struktur durchläuft und Stück für Stück freigibt, dient dies keinem nützlichen Zweck und verschwendet Ressourcen. Betrachten Sie insbesondere den Fall, in dem die Speicherseiten, die Ihren Cache enthalten, vom Betriebssystem auf die Festplatte ausgelagert wurden. Wenn Sie durch die Struktur gehen und sie freigeben, bringen Sie alle diese Seiten auf einmal wieder in den Speicher , verschwenden viel Zeit und Energie ohne wirklichen Nutzen und führen möglicherweise sogar dazu, dass andere Programme auf dem System ausgetauscht werden!

Als verwandtes Beispiel gibt es Hochleistungsserver, bei denen für jede Anforderung ein Prozess erstellt und nach Abschluss beendet wird. auf diese Weise sie müssen nicht einmal Speicher verfolgen Zuordnung , und nie überhaupt eine Befreiung oder Garbage Collection zu tun, da alles nur zurück in die am Ende des Prozesses freier Speicher Betriebssystem verschwindet. (Dasselbe kann innerhalb eines Prozesses mit einem benutzerdefinierten Speicherzuweiser ausgeführt werden, erfordert jedoch eine sehr sorgfältige Programmierung; im Wesentlichen muss man sich im Betriebssystemprozess eine eigene Vorstellung von „Lightweight-Prozessen“ machen.)

Kevin Reid
quelle
11

Ich entschuldige mich dafür, dass ich so lange nach dem letzten Beitrag in diesem Thread gepostet habe.

Ein zusätzlicher Punkt. Nicht alle Programme erreichen anmutige Exits. Abstürze und Strg-Cs usw. führen dazu, dass ein Programm unkontrolliert beendet wird. Wenn Ihr Betriebssystem Ihren Heap nicht freigeben, Ihren Stack bereinigen, statische Variablen usw. löschen würde, würde Ihr System möglicherweise aufgrund von Speicherlecks oder Schlimmerem abstürzen.

Abgesehen davon haben Abstürze / Unterbrechungen in Ubuntu und ich vermute, dass alle anderen modernen Betriebssysteme Probleme mit "behandelten" Ressourcen haben. Sockets, Dateien, Geräte usw. können "offen" bleiben, wenn ein Programm endet / abstürzt Es empfiehlt sich auch, alles im Rahmen Ihrer Bereinigung vor dem ordnungsgemäßen Beenden mit einem "Handle" oder "Deskriptor" zu schließen.

Ich entwickle derzeit ein Programm, das Sockets stark nutzt. Wenn ich in einem Hang stecken bleibe, muss ich die Strg-C-Taste drücken, um meine Steckdosen zu stranden. Ich habe einen std :: vector hinzugefügt, um eine Liste aller geöffneten Sockets und einen Sigaction-Handler zu sammeln, der Sigint und Sigterm abfängt. Der Handler geht die Liste durch und schließt die Sockets. Ich habe vor, vor dem Werfen eine ähnliche Bereinigungsroutine durchzuführen, die zu einer vorzeitigen Beendigung führt.

Möchte jemand diesen Entwurf kommentieren?

Wes Miller
quelle
1
Ich bin froh, dass Sie dies gesagt haben, weil ich ein Programm hatte, das Socket-Ressourcen übrig ließ, und unser Ubuntu-System alle zwei Wochen neu gestartet werden musste oder der Speicher knapp wurde und genügend Speicher vorhanden ist. Ich bin nicht sicher, ob die Systemressourcen abgerissen werden, wenn Sie vergessen, sie zu bereinigen.
Octopusgrabbus
8
Stackoverflow ist kein Forum. Es ist nichts Falsches daran, eine alte Frage zu beantworten. meta.stackexchange.com/questions/20524/reviving-old-questions
mk12
6

Was hier ( in einem modernen Betriebssystem ) passiert , ist, dass Ihr Programm in einem eigenen "Prozess" ausgeführt wird. Dies ist eine Betriebssystementität, die über einen eigenen Adressraum, Dateideskriptoren usw. verfügt. Ihre mallocAufrufe weisen Speicher vom "Heap" oder nicht zugewiesenen Speicherseiten zu, die Ihrem Prozess zugewiesen sind.

Wenn Ihr Programm wie in diesem Beispiel endet, werden alle Ihrem Prozess zugewiesenen Ressourcen einfach vom Betriebssystem recycelt / heruntergefahren. Im Falle des Speichers werden alle Ihnen zugewiesenen Speicherseiten einfach als "frei" markiert und für die Verwendung anderer Prozesse recycelt. Seiten sind ein untergeordnetes Konzept als das, was Malloc handhabt. Infolgedessen werden die Besonderheiten von Malloc / Free einfach weggespült, wenn das Ganze aufgeräumt wird.

Es ist das moralische Äquivalent dazu, dass Sie sich nicht die Mühe machen, jede Datei einzeln zu löschen, wenn Sie mit Ihrem Laptop fertig sind und ihn einem Freund geben möchten. Sie formatieren einfach die Festplatte.

Wie alle anderen Antwortenden bemerken, ist es jedoch keine gute Praxis, sich darauf zu verlassen:

  1. Sie sollten immer programmieren, um Ressourcen zu schonen, und in C bedeutet das auch Speicher. Möglicherweise wird Ihr Code in eine Bibliothek eingebettet, oder er läuft viel länger als erwartet.
  2. Einige Betriebssysteme (ältere und möglicherweise einige moderne eingebettete) behalten möglicherweise nicht so harte Prozessgrenzen bei, und Ihre Zuweisungen können sich auf die Adressräume anderer auswirken.
Ben Zotto
quelle
4

Ja. Das Betriebssystem bereinigt Ressourcen. Nun ... alte Versionen von NetWare haben es nicht getan.

Bearbeiten: Wie San Jacinto betonte, gibt es sicherlich Systeme (außer NetWare), die dies nicht tun. Selbst in Wegwerfprogrammen versuche ich, mir die Gewohnheit zu machen, alle Ressourcen freizugeben, nur um die Gewohnheit aufrechtzuerhalten.

Mark Wilkins
quelle
3
Ich stimme nicht ab, aber dies ist ein ziemlich gefährlicher Beitrag für die Nachwelt. DOS wird immer noch auf vielen eingebetteten Plattformen verwendet, und ich bezweifle ernsthaft, dass es die Speicherbereinigung für Sie durchführt. Die pauschale Verallgemeinerung ist falsch.
San Jacinto
@ San Jacinto: Das ist ein guter Punkt. Aus diesem Grund habe ich die NetWare-Referenz erstellt, die jedoch möglicherweise einer Klarstellung bedarf. Ich werde es ein bisschen bearbeiten.
Mark Wilkins
3
@San DOS ist kein Multitasking-Betriebssystem. Wenn ein DOS-Programm (ohne TSRs) endet, steht der gesamte Speicher für das nächste zu ladende Programm zur Verfügung.
@Neil danke für die Erinnerung, aber ich bezog mich auf ein TSR-ähnliches Programm, das gestartet werden würde, wenn ein Ereignis eintritt, wie es für eingebettete Systeme üblich ist. Trotzdem, danke für Ihr Fachwissen und Ihre Klarstellung, wo ich versagt habe :)
San Jacinto
2

Ja, das Betriebssystem gibt den gesamten Speicher frei, wenn der Prozess endet.

Draemon
quelle
Ich verstehe nicht, warum dies abgelehnt wurde. malloc'ed Speicher wird freigegeben, wenn der Prozess stirbt (die Wikipedia-Definition von malloc sagt dies)
Arve
7
Wikipedia ist nicht das Handbuch für jedes existierende Betriebssystem. Die meisten modernen Betriebssysteme werden den Speicher zurückgewinnen, aber nicht alle (und insbesondere nicht alle alten). Hinzu kommt, mallockann nur versprechen, was C mit dem Speicher machen wird; Von Natur aus garantiert C nicht viel in Bezug auf Verhalten außerhalb von C selbst. Wenn die App unerwartet stirbt, sind alle Versprechen, die die Laufzeitbibliothek gemacht hat, null und nichtig, da es nicht mehr lebendig ist, sie zu erfüllen.
CHao
2

Es hängt davon ab, dass Betriebssysteme es normalerweise für Sie bereinigen. Wenn Sie jedoch beispielsweise an eingebetteter Software arbeiten, wird es möglicherweise nicht freigegeben.

Stellen Sie einfach sicher, dass Sie es freigeben. Dies kann Ihnen später viel Zeit sparen, wenn Sie es in ein großes Projekt integrieren möchten.

Charles
quelle
0

Das hängt wirklich vom Betriebssystem ab, aber für alle Betriebssysteme, auf die Sie jemals stoßen werden, verschwindet die Speicherzuordnung, wenn der Prozess beendet wird.


quelle
0

Ich denke, direkte Befreiung ist am besten. Undefiniertes Verhalten ist das Schlimmste. Wenn Sie also Zugriff haben, während es noch in Ihrem Prozess definiert ist, gibt es viele gute Gründe, die die Leute dafür angegeben haben.

Wo oder ob ich das in W98 gefunden habe, war die eigentliche Frage 'wann' (ich habe keinen Beitrag gesehen, der dies betont). Ein kleines Vorlagenprogramm (für MIDI-SysEx-Eingaben unter Verwendung verschiedener malloc'd Leerzeichen) würde Speicher im WM_DESTROY-Bit des WndProc freigeben, aber als ich dies in ein größeres Programm transplantierte, stürzte es beim Beenden ab. Ich nahm an, dass dies bedeutete, dass ich versuchte, das freizugeben, was das Betriebssystem bereits während einer größeren Bereinigung freigegeben hatte. Wenn ich es auf WM_CLOSE gemacht habe, dann DestroyWindow () genannt, hat alles gut funktioniert, sofort sauberes Beenden.

Dies ist zwar nicht genau dasselbe wie bei MIDI-Puffern, es besteht jedoch eine Ähnlichkeit darin, dass es am besten ist, den Prozess intakt zu halten, vollständig zu bereinigen und dann zu beenden. Mit bescheidenen Speicherblöcken ist dies sehr schnell. Ich fand heraus, dass viele kleine Puffer im Betrieb und bei der Bereinigung schneller arbeiteten als weniger große.

Es kann Ausnahmen geben, wie jemand sagte, wenn vermieden wird, große Speicherblöcke aus einer Auslagerungsdatei auf der Festplatte zurückzuholen, aber selbst dies kann minimiert werden, indem mehr und kleinere zugewiesene Speicherplätze beibehalten werden.


quelle