Wie weit können Speicherverluste gehen?


118

Ich bin oft auf Speicherlecks gestoßen. Normalerweise, wenn ich so bin, mallocals gäbe es kein Morgen, oder wenn ich FILE *wie schmutzige Wäsche baumle. Ich gehe im Allgemeinen davon aus (sprich: hoffe verzweifelt), dass der gesamte Speicher zumindest nach Beendigung des Programms bereinigt wird. Gibt es Situationen, in denen durchgesickerter Speicher nicht erfasst wird, wenn das Programm beendet wird oder abstürzt?

Wenn die Antwort von Sprache zu Sprache sehr unterschiedlich ist, konzentrieren wir uns auf C (++).

Bitte beachten Sie die hyperbolische Verwendung des Ausdrucks "als gäbe es kein Morgen" und "baumeln ... wie schmutzige Wäsche". Unsicheres * malloc* kann diejenigen verletzen, die Sie lieben. Seien Sie auch bei schmutziger Wäsche vorsichtig.


3
Wenn Sie mit einem "modernen" Betriebssystem wie Linux oder Windows arbeiten, löst das Betriebssystem selbst nicht freigegebenen Speicher auf, wenn das Programm beendet wird.
Oliver Charlesworth

60
Anstatt zu mallocieren, als gäbe es kein Morgen, tun Sie so, als gäbe es ein Morgen und behalten Sie Ihr Gedächtnis im Auge!
William Pursell

8
@ WilliamPursell ah, du sagst also, man sollte callocgerne kein Morgen haben. Ausgezeichnet.
DilithiumMatrix

8
"Wenn die Antwort von Sprache zu Sprache sehr unterschiedlich ist, konzentrieren wir uns auf c (++)." c und c ++ sind nicht die gleiche Sprache!
Johnsyweb

11
@zhermes: Der Kommentar, dass C und C ++ unterschiedliche Sprachen sind, verbirgt sich mehr als Sie denken ... In C ++ nutzen Sie lieber Objekte mit automatischer Speicherdauer, folgen Sie der RAII-Sprache ... Sie lassen diese Objekte sich um den Speicher kümmern Management für Sie.
LihO

Antworten:


111

Betriebssysteme geben alle Ressourcen frei, die von Prozessen beim Beenden gehalten werden.

Dies gilt für alle Ressourcen, die das Betriebssystem verwaltet: Speicher, geöffnete Dateien, Netzwerkverbindungen, Fensterhandles ...

Wenn das Programm jedoch auf einem eingebetteten System ohne Betriebssystem oder mit einem sehr einfachen oder fehlerhaften Betriebssystem ausgeführt wird, kann der Speicher bis zu einem Neustart unbrauchbar werden. Aber wenn Sie in dieser Situation wären, würden Sie diese Frage wahrscheinlich nicht stellen.

Es kann lange dauern, bis das Betriebssystem bestimmte Ressourcen freigibt. Beispielsweise kann es Minuten dauern, bis der TCP-Port, den ein Netzwerkserver zum Akzeptieren von Verbindungen verwendet, frei wird, selbst wenn er vom Programm ordnungsgemäß geschlossen wird. Ein Netzwerkprogramm kann auch entfernte Ressourcen wie Datenbankobjekte enthalten. Das Remote-System sollte diese Ressourcen freigeben, wenn die Netzwerkverbindung unterbrochen wird. Dies kann jedoch noch länger dauern als das lokale Betriebssystem.


5
Ein gängiges Paradigma in RTOSs ist das Einzelprozess-, Mehrfach-Thread-Modell und kein Speicherschutz zwischen "Aufgaben". Es gibt normalerweise einen Haufen. So hat VxWorks sicherlich funktioniert - und wahrscheinlich auch noch.
Marko

29
Beachten Sie, dass nicht alle Ressourcen vom Betriebssystem freigegeben werden können. Netzwerkverbindungen, Datenbanktransaktionen usw., die nicht explizit geschlossen werden, können zu unerwünschten Ergebnissen führen. Wenn die Netzwerkverbindung nicht geschlossen wird, glaubt der Server möglicherweise, dass Sie auf unbestimmte Zeit noch aktiv sind. Bei Servern, die die Anzahl der aktiven Verbindungen begrenzen, kann dies versehentlich zu einem Denial-of-Service führen. Wenn Sie Datenbanktransaktionen nicht schließen, können Sie nicht festgeschriebene Daten verlieren.
Lie Ryan

1
@Marko: Die neueste Version von vxWorks unterstützt jetzt RTPs (Echtzeitprozesse), die den Speicherschutz unterstützen.
Xavier T.

20
"Betriebssysteme geben alle Ressourcen frei, die von Prozessen beim Beenden gehalten werden." Nicht unbedingt wahr. Beispielsweise werden unter (mindestens) Linux SysV-Semaphoren und andere IPC-Objekte beim Beenden des Prozesses nicht bereinigt. Aus diesem Grund gibt es ipcrmfür die manuelle Bereinigung linux.die.net/man/8/ipcrm .
Sleske

7
Wenn ein Objekt über eine temporäre Datei verfügt, die es verwaltet, wird diese anschließend eindeutig nicht mehr bereinigt.
Mooing Duck

47

Der C-Standard legt nicht fest, dass der von zugewiesene Speicher mallocfreigegeben wird, wenn das Programm beendet wird. Dies geschieht durch das Betriebssystem und nicht alle Betriebssysteme (normalerweise in der eingebetteten Welt) geben den Speicher frei, wenn das Programm beendet wird.


20
Das liegt mehr oder weniger daran, dass der C-Standard von C-Programmen spricht, nicht von den Betriebssystemen, auf denen C ausgeführt wird ...
vonbrand

5
@vonbrand Der C-Standard könnte einen Absatz enthalten haben, der besagt, dass bei der mainRückgabe der gesamte von zugewiesene Speicher mallocfreigegeben wird. Beispielsweise heißt es, dass alle geöffneten Dateien vor dem Beenden des Programms geschlossen werden. Für den mir zugewiesenen Speicher mallocist er einfach nicht angegeben. Jetzt beschreibt mein Satz über das Betriebssystem natürlich, was normalerweise getan wird, nicht was der Standard vorschreibt, da er nichts dazu spezifiziert.
Ouah

Lassen Sie mich meinen Kommentar korrigieren: Der Standard spricht von C, nicht davon, wie das Programm gestartet und gestoppt wird. Sie können sehr gut ein C-Programm schreiben, das ohne Betriebssystem ausgeführt wird. In diesem Fall gibt es niemanden, der aufräumt. Der Standard spezifiziert ganz bewusst nichts, es sei denn, dies wird benötigt, um die Verwendung nicht ohne Notwendigkeit einzuschränken.
vonbrand

2
@ouah: " wenn main zurückkommt ...". Das ist eine Annahme. Wir müssen überlegen, ob die Hauptrendite ... ist. std::atexitBerücksichtigt auch die Programmbeendigung über std::exit, und dann gibt es auch std::abortund (C ++ spezifisch) std::terminate.
MSalters

@ouah: Wenn das enthalten gewesen atexitwäre , wäre nicht verwendbar. :-)
R .. GitHub STOP HELPING ICE

28

Da alle Antworten die meisten Aspekte Ihrer Frage zu modernen Betriebssystemen abgedeckt haben, gibt es historisch gesehen einen, der erwähnenswert ist, wenn Sie jemals in der DOS-Welt programmiert haben. TSR-Programme ( Terminant and Stay Resident ) geben normalerweise die Kontrolle an das System zurück, befinden sich jedoch im Speicher, der durch einen Software- / Hardware-Interrupt wiederbelebt werden könnte. Es war normal, dass bei der Arbeit mit diesen Betriebssystemen Meldungen wie "Nicht genügend Speicher! Versuchen Sie, einige Ihrer TSRs zu entladen" angezeigt wurden.

Technisch gesehen wird das Programm beendet , aber da es sich immer noch im Speicher befindet, wird kein Speicherverlust ausgelöst, es sei denn, Sie entladen das Programm.

Sie können dies also als einen weiteren Fall betrachten, abgesehen davon, dass Betriebssysteme keinen Speicher zurückfordern, entweder weil es fehlerhaft ist oder weil das eingebettete Betriebssystem dafür ausgelegt ist.

Ich erinnere mich an ein weiteres Beispiel. Das Customer Information Control System (CICS), ein Transaktionsserver, der hauptsächlich auf IBM Mainframes ausgeführt wird, ist pseudokonversativ. Bei der Ausführung werden die vom Benutzer eingegebenen Daten verarbeitet, ein weiterer Datensatz für den Benutzer generiert, an den Benutzerendknoten übertragen und beendet. Beim Aktivieren der Aufmerksamkeitstaste wird sie erneut aktiviert, um einen anderen Datensatz zu verarbeiten. Aufgrund des technischen Verhaltens kann das Betriebssystem keinen Speicher von den abgeschlossenen CICS-Programmen zurückfordern, es sei denn, Sie recyceln den CICS-Transaktionsserver.


Das ist wirklich interessant, danke für die historische Notiz! Wissen Sie, ob dieses Paradigma darauf zurückzuführen ist, dass die Speicherfreigabe zu rechenintensiv ist, wenn dies nicht erforderlich ist? Oder war an die Alternative noch nie gedacht worden?
DilithiumMatrix

1
@zhermes: Es war rechnerisch unmöglich, da DOS die Speicherzuordnungen für TSRs einfach nicht verfolgte. Ziemlich per Definition: Das Ziel war es, Resident zu bleiben . Wenn Sie möchten, dass Ihr TSR etwas, aber nicht den gesamten Speicher freigibt, liegt es an Ihnen, zu entscheiden, was freigegeben werden soll.
MSalters

2
@zhermes: DOS (wie CP / M, sein Vorfahr) war nicht das, was man als Betriebssystem im modernen Sinne bezeichnen würde. Es war wirklich nur eine Sammlung von E / A-Dienstprogrammen, die auf standardmäßige Weise aufgerufen werden konnten und mit einem Befehlsprozessor gebündelt waren, mit dem Sie jeweils ein Programm ausführen konnten. Es gab keine Vorstellung von Prozessen und der Speicher war weder virtuell noch geschützt. TSRs waren ein nützlicher Hack, der dem System mitteilen konnte, dass sie bis zu 64 KB Speicherplatz beanspruchten und sich in Interrupts einhielten, damit sie aufgerufen wurden.
Blrfl

8

Wie die anderen bereits gesagt haben, werden die meisten Betriebssysteme nach Beendigung des Prozesses den zugewiesenen Speicher zurückfordern (und wahrscheinlich auch andere Ressourcen wie Netzwerksockets, Dateihandles usw.).

Allerdings ist der Speicher möglicherweise nicht das einzige, worüber Sie sich Sorgen machen müssen, wenn Sie mit new / delete (anstelle von raw malloc / free) arbeiten. Der in new zugewiesene Speicher wird möglicherweise zurückgefordert, aber Dinge, die in den Destruktoren der Objekte ausgeführt werden können, werden nicht ausgeführt. Vielleicht schreibt der Destruktor einer Klasse bei Zerstörung einen Sentinel-Wert in eine Datei. Wenn der Prozess gerade beendet wird, wird das Dateihandle möglicherweise geleert und der Speicher zurückgefordert, aber dieser Sentinel-Wert wird nicht geschrieben.

Moral der Geschichte, immer nach sich selbst aufräumen. Lass die Dinge nicht baumeln. Verlassen Sie sich nicht darauf, dass das Betriebssystem nach Ihnen bereinigt wird. Räumen Sie nach sich selbst auf.


»Verlassen Sie sich nicht darauf, dass das Betriebssystem nach Ihnen bereinigt. Räumen Sie nach sich auf. ' Dies ist bei komplexen Multithread-Apps oftmals sehr "sehr, sehr schwierig". Tatsächliche Lecks, bei denen alle Verweise auf eine Ressource verloren gegangen sind, sind schlecht. Es ist nicht immer schlecht und oft der einzig vernünftige Weg, dem Betriebssystem das Bereinigen zu erlauben, anstatt Referenzen explizit freizugeben.
Martin James

1
In C ++ Destruktoren wird erhalten bei Beendigung des Programms (es sei denn , einige weniger als helle genannt kill -9Fan zeigt nach oben ...)
vonbrand

@vonbrand Stimmt, aber wenn es sich um Lecks mit dynamischen Objekten handelt, treten diese Destruktoren nicht auf. Das Objekt, das den Gültigkeitsbereich verlässt, ist ein Rohzeiger, und sein Destruktor ist ein No-Op. (Natürlich siehe RAII-Objekte, um dieses Problem zu
beheben

1
Das Problem mit RAII besteht darin, dass es darauf besteht, Objekte beim Beenden des Prozesses freizugeben, deren Beseitigung eigentlich nicht wichtig ist. DB-Verbindungen, mit denen Sie vorsichtig sein möchten, aber der allgemeine Speicher wird am besten vom Betriebssystem bereinigt (es macht einen weitaus besseren Job). Das Problem manifestiert sich in einem Programm, dessen Beenden absolut ewig dauert , sobald der ausgelagerte Speicherplatz aufgebraucht ist. Es ist auch nicht trivial zu lösen ...
Donal Fellows

@vonbrand: So einfach ist das nicht. std::exitwird dtors anrufen, std::abortwird nicht, ungefangene Ausnahmen könnten.
MSalters

7

Dies hängt eher vom Betriebssystem als von der Sprache ab. Letztendlich erhält jedes Programm in einer beliebigen Sprache seinen Speicher vom Betriebssystem.

Ich habe noch nie von einem Betriebssystem gehört, das beim Beenden / Abstürzen eines Programms keinen Speicher recycelt. Wenn Ihr Programm also eine Obergrenze für den Speicher hat, den es zuweisen muss, ist es durchaus sinnvoll, nur zuzuweisen und niemals freizugeben.


Könnten Sie das Speicherbild des Kernels im Falle eines vereinfachten Betriebssystems vermasseln? .. Wie diese Betriebssysteme ohne Multitasking.
Ulidtko

@ulidtko, das wird alles vermasseln . Wenn mein Programm ab und zu beispielsweise 1 GB benötigt und dies für die Dauer erfasst, verweigert es anderen die Verwendung dieser Ressourcen, auch wenn sie nicht verwendet werden. Das könnte heute wichtig sein oder nicht. Aber die Umwelt wird sich radikal verändern. Garantiert.
vonbrand

@vonbrand Die seltene Verwendung von 1 GB ist normalerweise kein Problem (solange Sie über genügend physischen Speicher verfügen), da moderne Betriebssysteme die derzeit nicht aktiven Bits ausblenden können. Das Problem tritt auf, wenn mehr virtueller Speicher aktiv genutzt wird als physischer Speicher, in dem er gehostet werden kann.
Donal Fellows

5

Wenn das Programm jemals in eine dynamische Komponente ("Plugin") umgewandelt wird, die in den Adressraum eines anderen Programms geladen wird, ist dies selbst unter einem Betriebssystem mit ordentlicher Speicherverwaltung problematisch. Wir müssen nicht einmal daran denken, dass der Code auf weniger leistungsfähige Systeme portiert wird.

Auf der anderen Seite, den gesamten Speicher Freigabe kann die Leistung eines Bereinigungsprogramm auswirken.

Bei einem Programm, an dem ich gearbeitet habe, benötigte ein bestimmter Testfall 30 Sekunden oder länger, bis das Programm beendet wurde, da es sich durch das Diagramm des gesamten dynamischen Speichers bewegte und es Stück für Stück freigab.

Eine vernünftige Lösung besteht darin, die Fähigkeit dort zu haben und sie mit Testfällen abzudecken, sie jedoch im Produktionscode auszuschalten, damit die Anwendung schnell beendet wird.


5

Alle Betriebssysteme, die den Titel verdienen, bereinigen das Durcheinander, das Ihr Prozess nach der Beendigung verursacht hat. Aber es gibt immer unvorhergesehene Ereignisse. Was ist, wenn der Zugriff irgendwie verweigert wurde und ein armer Programmierer die Möglichkeit nicht vorausgesehen hat und es daher nicht ein bisschen später erneut versucht? Es ist immer sicherer, sich selbst zu bereinigen, wenn Speicherlecks geschäftskritisch sind - ansonsten lohnt sich die Mühe IMO nicht wirklich, wenn diese Mühe kostspielig ist.

Bearbeiten: Sie müssen Speicherlecks beseitigen, wenn sie sich dort befinden, wo sie sich ansammeln, wie in Schleifen. Die Speicherlecks, von denen ich spreche, sind solche, die sich im Laufe des Programms in konstanter Zeit aufbauen. Wenn Sie ein Leck anderer Art haben, wird es höchstwahrscheinlich früher oder später ein ernstes Problem sein.

In technischer Hinsicht sind Ihre Lecks, wenn sie von Speicherkomplexität sind (O), in den meisten Fällen in Ordnung, O (logn) bereits unangenehm (und in einigen Fällen tödlich) und O (N) + unerträglich.


3

Der gemeinsam genutzte Speicher auf POSIX-kompatiblen Systemen bleibt bestehen, bis shm_unlink aufgerufen oder das System neu gestartet wird.


2

Wenn Sie über eine Interprozesskommunikation verfügen, kann dies dazu führen, dass andere Prozesse je nach Protokoll niemals Ressourcen abschließen und verbrauchen.

Als Beispiel habe ich einmal mit dem Drucken auf einem PDF-Drucker in Java experimentiert, als ich die JVM mitten in einem Druckerauftrag beendet habe, der PDF-Spooling-Prozess aktiv blieb und ich ihn im Task-Manager beenden musste, bevor ich konnte Wiederholen Sie den Druck.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.