665 Stimmen

Was passiert WIRKLICH, wenn man nach malloc nicht frei gibt, bevor das Programm beendet wird?

Uns allen wurde beigebracht, dass man jeden zugewiesenen Zeiger freigeben MUSS. Ich bin allerdings ein wenig neugierig, was es wirklich kostet, wenn man den Speicher nicht freigibt. In einigen offensichtlichen Fällen, etwa wenn malloc() innerhalb einer Schleife oder eines Teils einer Thread-Ausführung aufgerufen wird, ist es sehr wichtig, den Speicher freizugeben, damit keine Speicherlecks entstehen. Aber betrachten Sie die folgenden zwei Beispiele:

Erstens, wenn ich einen Code habe, der in etwa so aussieht:

int main()
{
    char *a = malloc(1024);
    /* Do some arbitrary stuff with 'a' (no alloc functions) */
    return 0;
}

Was ist das wirkliche Ergebnis hier? Ich denke, dass der Prozess stirbt und dann der Heap-Speicherplatz sowieso weg ist, so dass es keinen Schaden gibt, wenn der Aufruf von free (Ich erkenne jedoch an, wie wichtig es ist, sie trotzdem zu haben, um sie zu schließen, zu warten und um eine gute Praxis zu haben). Liege ich mit dieser Überlegung richtig?

Zweitens: Nehmen wir an, ich habe ein Programm, das ein wenig wie eine Shell funktioniert. Benutzer können Variablen deklarieren wie aaa = 123 und diese werden zur späteren Verwendung in einer dynamischen Datenstruktur gespeichert. Es liegt auf der Hand, dass Sie eine Lösung verwenden, die eine *alloc-Funktion aufruft (Hashmap, verknüpfte Liste oder etwas Ähnliches). Für diese Art von Programm macht es keinen Sinn, nach dem Aufruf von malloc weil diese Variablen während der Ausführung des Programms jederzeit vorhanden sein müssen und es keine gute Möglichkeit gibt (die ich sehen kann), dies mit statisch zugewiesenem Speicherplatz zu implementieren. Ist es schlechtes Design, um einen Haufen Speicher zu haben, der zugewiesen wird, aber nur als Teil der Prozessbeendigung freigegeben wird? Wenn ja, was ist die Alternative?

26voto

Tim Post Punkte 32750

In der Regel gebe ich jeden zugewiesenen Block frei, sobald ich sicher bin, dass ich mit ihm fertig bin. Heute könnte der Einstiegspunkt meines Programms sein main(int argc, char *argv[]) aber morgen könnte es sein foo_entry_point(char **args, struct foo *f) und als Funktionszeiger typisiert.

Wenn das also passiert, habe ich jetzt ein Leck.

Zu Ihrer zweiten Frage: Wenn mein Programm eine Eingabe wie a=5 annimmt, würde ich Platz für a zuweisen oder denselben Platz bei einem nachfolgenden a="foo" erneut zuweisen. Dieser Platz bliebe so lange reserviert, bis:

  1. Der Benutzer hat 'unset a' eingegeben
  2. Meine Aufräumfunktion wurde eingegeben, entweder durch ein Signal oder durch die Eingabe von 'quit' durch den Benutzer

Ich kann mich nicht an eine modern Betriebssystem, das nach Beendigung eines Prozesses keinen Speicher zurückfordert. Andererseits ist free() billig, warum nicht aufräumen? Wie andere schon sagten, sind Tools wie valgrind großartig, um Lecks zu finden, um die man sich wirklich kümmern muss. Auch wenn die Blöcke, die Sie als Beispiel anführen, als "noch erreichbar" bezeichnet werden, ist das nur ein zusätzliches Rauschen in der Ausgabe, wenn Sie versuchen, sicherzustellen, dass Sie keine Lecks haben.

Ein weiterer Mythos ist " Wenn es in main() ist, muss ich es nicht freigeben ", ist dies falsch. Betrachten Sie das Folgende:

char *t;

for (i=0; i < 255; i++) {
    t = strdup(foo->name);
    let_strtok_eat_away_at(t);
}

Wenn das vor dem Forken / Daemonisieren geschah (und theoretisch ewig läuft), hat Ihr Programm gerade eine unbestimmte Größe von t 255 Mal geleakt.

Ein gutes, gut geschriebenes Programm sollte immer hinter sich selbst aufräumen. Den gesamten Speicher freigeben, alle Dateien leeren, alle Deskriptoren schließen, alle temporären Dateien entkoppeln usw. Diese Aufräumfunktion sollte bei normaler Beendigung oder beim Empfang verschiedener Arten von fatalen Signalen erreicht werden, es sei denn, Sie wollen einige Dateien herumliegen lassen, um einen Absturz zu erkennen und fortzusetzen.

Seien Sie wirklich nett zu der armen Seele, die Ihre Sachen instand halten muss, wenn Sie sich anderen Dingen zuwenden geben Sie es ihnen 'valgrind clean' :)

16voto

Antti Huima Punkte 24441

Es ist völlig in Ordnung, den Speicher beim Beenden nicht freizugeben; malloc() weist den Speicher aus dem "Heap" genannten Speicherbereich zu, und der gesamte Heap eines Prozesses wird freigegeben, wenn der Prozess beendet wird.

Ein Grund, warum Leute immer noch darauf bestehen, dass es gut ist, alles vor dem Beenden freizugeben, ist, dass Speicherdebugger (z.B. valgrind unter Linux) die nicht freigegebenen Blöcke als Speicherlecks erkennen, und wenn man auch "echte" Speicherlecks hat, wird es schwieriger, sie zu erkennen, wenn man am Ende auch "falsche" Ergebnisse erhält.

14voto

sharptooth Punkte 162790

Dieser Code wird in der Regel gut funktionieren, aber bedenken Sie das Problem der Wiederverwendung von Code.

Vielleicht haben Sie einen Codeschnipsel geschrieben, der den zugewiesenen Speicher nicht freigibt, sondern so ausgeführt wird, dass der Speicher automatisch wieder freigegeben wird. Scheint in Ordnung zu sein.

Dann kopiert ein anderer Ihr Snippet so in sein Projekt, dass es tausendmal pro Sekunde ausgeführt wird. Diese Person hat nun ein riesiges Speicherleck in seinem Programm. Im Allgemeinen nicht sehr gut, für eine Serveranwendung in der Regel fatal.

Die Wiederverwendung von Code ist in Unternehmen typisch. Normalerweise ist das Unternehmen Eigentümer des gesamten von seinen Mitarbeitern erstellten Codes, und jede Abteilung kann das, was dem Unternehmen gehört, wiederverwenden. Wenn Sie also einen solchen "harmlos aussehenden" Code schreiben, bereiten Sie anderen Leuten potenzielle Kopfschmerzen. Das kann dazu führen, dass Sie gefeuert werden.

14voto

DevSolar Punkte 63096

Was ist das wirkliche Ergebnis hier?

Ihr Programm hat den Speicher geleert. Je nach Betriebssystem kann es Mai wiederhergestellt worden sind.

Die meisten modernen Desktop Betriebssysteme tun den ausgelaufenen Speicher beim Beenden des Prozesses wiederherzustellen, so dass es leider üblich ist, das Problem zu ignorieren (wie aus vielen anderen Antworten hier ersichtlich ist).

Aber Sie verlassen sich auf eine Sicherheitsfunktion, auf die Sie sich nicht verlassen sollten, und Ihr Programm (oder Ihre Funktion) könnte auf einem System laufen, auf dem dieses Verhalten tut zu einem "harten" Speicherleck führen, nächste Zeit.

Möglicherweise laufen Sie im Kernel-Modus oder auf alten/eingebetteten Betriebssystemen, die keinen Speicherschutz als Kompromiss einsetzen. (MMUs benötigen Platz auf dem Chip, Speicherschutz kostet zusätzliche CPU-Zyklen, und es ist nicht zu viel verlangt, dass ein Programmierer hinter sich selbst aufräumt).

Sie können den Speicher beliebig verwenden und wiederverwenden, aber stellen Sie sicher, dass Sie vor dem Beenden alle Ressourcen freigegeben haben.

11voto

Bill the Lizard Punkte 384619

Wenn Sie den von Ihnen zugewiesenen Speicher verwenden, machen Sie nichts falsch. Problematisch wird es, wenn Sie Funktionen (außer main) schreiben, die Speicher zuweisen, ohne ihn freizugeben und ohne ihn dem Rest Ihres Programms zur Verfügung zu stellen. Dann läuft Ihr Programm mit dem ihm zugewiesenen Speicher weiter, ohne dass Sie ihn nutzen können. Ihr Programm und andere laufende Programme werden dieser Erinnerung beraubt.

Edit: Es ist nicht 100%ig korrekt zu sagen, dass anderen laufenden Programmen dieser Speicher vorenthalten wird. Das Betriebssystem kann ihn immer auf Kosten der Auslagerung Ihres Programms in den virtuellen Speicher verwenden ( </handwaving> ). Der Punkt ist jedoch, dass ein Auslagern des virtuellen Speichers weniger wahrscheinlich ist, wenn Ihr Programm Speicher freigibt, den es nicht benutzt.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X