Ist es jemals in Ordnung, free() für zugewiesenen Speicher *nicht* zu verwenden?

Lesezeit: 11 Minuten

Benutzeravatar von Nick
Nick

Ich studiere Informatik und habe einige Elektronikkurse. Ich habe von zwei meiner Professoren (dieser Kurse) gehört, dass es möglich ist, die Verwendung von zu vermeiden free() Funktion (nach malloc(), calloc()usw.), da die zugewiesenen Speicherplätze wahrscheinlich nicht erneut verwendet werden, um anderen Speicher zuzuweisen. Das heißt, wenn Sie beispielsweise 4 Bytes zuweisen und dann freigeben, haben Sie 4 Bytes Speicherplatz, die wahrscheinlich nicht erneut zugewiesen werden: Sie haben a Loch.

Ich finde das verrückt: Du kannst keine haben Nicht-Spielzeug-Programm wo Sie Speicher auf dem Heap zuweisen, ohne ihn freizugeben. Aber ich habe nicht das Wissen, um genau zu erklären, warum es so wichtig ist, dass für jeden malloc() es muss eine geben free().

Also: Gibt es Umstände, unter denen es angemessen sein könnte, a malloc() ohne zu benutzen free()? Und wenn nicht, wie kann ich das meinen Professoren erklären?

  • Sie sind nicht “falsch” – sie haben einen gültigen (wenn auch begrenzten) Punkt zur Fragmentierung sehr kleiner isolierter freier Regionen und haben ihn wahrscheinlich etwas vorsichtiger formuliert, als Sie ihn berichtet haben.

    – Chris Stratton

    18. März 2014 um 14:28 Uhr


  • Sie müssen Speicher nur dann nicht freigeben, wenn Sie verwalteten Speicher verwenden oder wenn Sie den ursprünglich zugewiesenen Speicher wiederverwenden möchten. Ich vermute, dass der Grund, warum zwei Ausbilder dies gesagt haben, darin besteht, dass Sie über ein bestimmtes Programm sprechen, das den Speicher wiederverwenden kann. In diesem Fall würden Sie immer noch free() verwenden, aber nur am Ende Ihrer Anwendung. Bist du sicher, dass das nicht ihre Bedeutung war?

    – krowe

    18. März 2014 um 15:01 Uhr

  • @Marian: Ich hatte einen Professor, der behauptete, dass es in C und C ++ notwendig ist, Ihren zugewiesenen Speicher in einer Funktion freizugeben, die in derselben .c/.cxx-Datei definiert ist, in der er zugewiesen wurde … Diese Leute scheinen manchmal stark unter Hypoxie zu leiden weil er zu hoch in einem Elfenbeinturm lebt.

    – PlasmaHH

    18. März 2014 um 20:22 Uhr

  • Es gibt einige Nicht-Spielzeug-Programme, die den Speicher nicht freigeben, und das Betriebssystem alles beim Beenden des Prozesses bereinigen zu lassen viel schneller als (umständlich) viele Buchhaltungen zu führen, damit Sie es selbst tun können.

    – Donal Fellows

    18. März 2014 um 23:42 Uhr

  • Fügen Sie niemals Dinge, die Sie gehört haben, ungefragt in Ihr Gehirn ein. Ich hatte viele Lehrer und Lektoren und Korrektoren, die falsch oder veraltet waren. Und immer genau analysieren, was sie sagen. Unsere Leute sind oft ziemlich genau und können Dinge sagen, die richtig sind, aber von jemandem, der sich nur im allgemeinen Sprachgebrauch zu Hause fühlt, leicht falsch oder mit falscher Priorität verstanden werden können. Ich erinnere mich zB, dass in der Schule ein Lehrer sagte “Hast du deine Hausaufgaben gemacht.”, ich sagte “Nein.”. Obwohl ich Recht hatte, fand der Lehrer das anstößig, weil ich mir die Zeit erspart hatte, faule Ausreden zu finden, mit denen er nicht gerechnet hatte.

    – Sebastian Mach

    19. März 2014 um 11:24 Uhr

Benutzeravatar von unwind
entspannen

Einfach: Lesen Sie einfach die Quelle von so ziemlich jedem halbwegs ernsten malloc()/free() Implementierung. Damit meine ich das Eigentliche Speichermanager die die Arbeit der Anrufe übernimmt. Dies kann sich in der Laufzeitbibliothek, der virtuellen Maschine oder dem Betriebssystem befinden. Natürlich ist der Code nicht in allen Fällen gleichermaßen zugänglich.

Sicherzustellen, dass der Speicher nicht fragmentiert wird, indem benachbarte Löcher zu größeren Löchern verbunden werden, ist sehr üblich. Seriösere Allokatoren verwenden seriösere Techniken, um dies sicherzustellen.

Nehmen wir also an, Sie führen drei Zuweisungen und Freigaben durch und erhalten Blöcke in dieser Reihenfolge im Speicher:

+-+-+-+
|A|B|C|
+-+-+-+

Dabei spielt die Größe der einzelnen Allokationen keine Rolle. dann befreist du den ersten und letzten, A und C:

+-+-+-+
| |B| |
+-+-+-+

Wenn Sie B schließlich befreien, erhalten Sie (zunächst zumindest theoretisch) Folgendes:

+-+-+-+
| | | |
+-+-+-+

die einfach defragmentiert werden können

+-+-+-+
|     |
+-+-+-+

dh ein einzelner größerer freier Block, keine Fragmente übrig.

Referenzen, wie gewünscht:

  • Versuchen Sie, den Code für zu lesen dlmalloc. Ich bin viel weiter fortgeschritten, da ich eine vollständige Implementierung in Produktionsqualität bin.
  • Auch in eingebetteten Anwendungen sind defragmentierende Implementierungen verfügbar. Siehe zum Beispiel diese Notizen auf der heap4.c Code in FreeRTOS.

  • Können Sie mir einige Referenzen nennen?

    – Nick

    18. März 2014 um 13:45 Uhr

  • Ich denke, es wäre erwähnenswert, dass der virtuelle Adressraum keine direkte Darstellung des physischen Speichers ist. Und diese Fragmentierung im physischen Speicher kann vom Betriebssystem behandelt werden, während virtueller Speicher, der nicht vom Prozess freigegeben wird, auch nicht physisch freigegeben wird.

    – lach

    18. März 2014 um 13:49 Uhr

  • @PetrBudnik Es wäre sowieso selten, dass der virtuelle Speicher 1: 1 dem physischen Speicher zugeordnet wird. Das Betriebssystem denkt an Seitenzuordnungen und kann ihn mit minimalem Aufwand ein- und auslagern

    – Ratschenfreak

    18. März 2014 um 14:33 Uhr

  • Sehr pingelig Kommentar: Während das Konzept gut erklärt ist, wurde das eigentliche Beispiel etwas unglücklich gewählt. Für alle, die sich den Quellcode von beispielsweise dlmalloc ansehen und verwirrt sind: Blöcke unterhalb einer bestimmten Größe sind immer Potenzen von 2 und werden entsprechend zusammengeführt / aufgeteilt. Wir würden also (möglicherweise) einen 8-Byte-Block und einen 4-Byte-Block haben, aber keinen 12-Byte-Block. Dies ist zumindest auf Desktops ein ziemlich üblicher Ansatz für Zuordner, obwohl eingebettete Anwendungen möglicherweise versuchen, mit ihrem Overhead vorsichtiger umzugehen.

    – Voo

    19. März 2014 um 1:24 Uhr

  • @Voo Ich habe die Erwähnung einer Größe für die Blöcke im Beispiel entfernt, es war sowieso egal. Besser?

    – abschalten

    19. März 2014 um 7:41 Uhr

Die anderen Antworten erklären bereits sehr gut, dass echte Implementierungen von malloc() und free() koaleszieren (defragmnieren) Löcher tatsächlich zu größeren freien Brocken. Aber selbst wenn das nicht der Fall wäre, wäre es immer noch eine schlechte Idee, darauf zu verzichten free().

Die Sache ist die, Ihr Programm hat gerade diese 4 Bytes Speicher zugewiesen (und freigeben wollen). Wenn es über einen längeren Zeitraum ausgeführt wird, ist es sehr wahrscheinlich, dass es nur 4 Bytes Speicher erneut zuweisen muss. Selbst wenn diese 4 Bytes niemals zu einem größeren zusammenhängenden Raum zusammenwachsen, können sie dennoch vom Programm selbst wiederverwendet werden.

  • +1 Genau. Die Sache ist, wenn free oft genug aufgerufen wird, um sich auf die Leistung auszuwirken, dann wird es wahrscheinlich auch so oft aufgerufen, dass das Weglassen eine sehr große Delle im verfügbaren Speicher verursacht. Es ist schwer vorstellbar, dass auf einem eingebetteten System die Leistung kontinuierlich darunter leidet free aber wo malloc wird nur endlich oft aufgerufen; Es ist ein ziemlich seltener Anwendungsfall, ein eingebettetes Gerät zu haben, das Daten in einem Schritt verarbeitet und dann zurücksetzt.

    – Jason C

    19. März 2014 um 8:36 Uhr


Benutzeravatar von Paul Evans
Paul Evans

Es ist totaler Unsinn, zum Beispiel gibt es viele verschiedene Implementierungen von malloceinige versuchen, den Haufen effizienter zu machen, wie Doug Leas oder Dies eines.

Arbeiten Ihre Professoren zufällig mit POSIX? Wenn sie daran gewöhnt sind, viele kleine, minimalistische Shell-Anwendungen zu schreiben, ist das ein Szenario, in dem ich mir vorstellen kann, dass dieser Ansatz nicht allzu schlecht wäre – den ganzen Haufen auf einmal nach Belieben des Betriebssystems freizugeben ist schneller als das Freigeben von tausend Variablen. Wenn Sie davon ausgehen, dass Ihre Anwendung ein oder zwei Sekunden lang ausgeführt wird, könnten Sie problemlos ohne die Aufhebung der Zuordnung davonkommen.

Es ist natürlich immer noch eine schlechte Praxis (Leistungsverbesserungen sollten immer auf Profiling basieren, nicht auf einem vagen Bauchgefühl), und es ist nichts, was Sie Studenten sagen sollten, ohne die anderen Einschränkungen zu erklären, aber ich kann mir eine Menge Tiny-Piping-Shell vorstellen -Anwendungen auf diese Weise geschrieben werden (wenn nicht direkt statische Zuweisung verwendet wird). Wenn Sie an etwas arbeiten, das davon profitiert, dass Sie Ihre Variablen nicht freigeben, arbeiten Sie entweder unter extrem niedrigen Latenzbedingungen (in diesem Fall, wie können Sie sich überhaupt dynamische Zuordnung und C++ leisten? :D), oder Sie tun es etwas sehr, sehr falsch machen (wie das Zuweisen eines Integer-Arrays durch Zuweisen von tausend Integern nacheinander anstelle eines einzelnen Speicherblocks).

Sie erwähnten, dass sie Elektronikprofessoren waren. Sie sind möglicherweise daran gewöhnt, Firmware/Echtzeit-Software zu schreiben, wobei es oft erforderlich ist, die Ausführung von Dingen genau zu timen. Wenn Sie in diesen Fällen wissen, dass Sie über genügend Speicher für alle Zuweisungen verfügen und Speicher nicht freigeben und neu zuweisen, kann dies zu einer einfacher zu berechnenden Begrenzung der Ausführungszeit führen.

In einigen Schemata kann auch ein Hardware-Speicherschutz verwendet werden, um sicherzustellen, dass die Routine in ihrem zugewiesenen Speicher abgeschlossen wird oder einen Trap in dem erzeugt, was sein sollte sehr Ausnahmefällen.

  • Das ist ein guter Punkt. Ich würde jedoch erwarten, dass sie es nicht verwenden würden malloc und so überhaupt in diesem Fall, anstatt sich auf die statische Zuweisung zu verlassen (oder vielleicht einen großen Teil zuzuweisen und dann den Speicher manuell zu handhaben).

    – Luan

    19. März 2014 um 8:38 Uhr

Benutzeravatar von Shaz
Shaz

Wenn Sie dies aus einem anderen Blickwinkel betrachten als frühere Kommentatoren und Antworten, besteht eine Möglichkeit darin, dass Ihre Professoren Erfahrungen mit Systemen gemacht haben, bei denen der Speicher statisch zugewiesen wurde (dh wenn das Programm kompiliert wurde).

Statische Zuordnung tritt auf, wenn Sie Folgendes tun:

define MAX_SIZE 32
int array[MAX_SIZE];

In vielen Echtzeit- und eingebetteten Systemen (die am ehesten von EEs oder CEs angetroffen werden) ist es normalerweise vorzuziehen, eine dynamische Speicherzuordnung insgesamt zu vermeiden. Also, Verwendungen von malloc, new, und ihre Deletionsgegenstücke sind selten. Hinzu kommt, dass der Speicher in Computern in den letzten Jahren explodiert ist.

Wenn Ihnen 512 MB zur Verfügung stehen und Sie 1 MB statisch zuweisen, müssen Sie ungefähr 511 MB durchrollen, bevor Ihre Software explodiert (na ja, nicht genau … aber gehen Sie hier mit mir). Angenommen, Sie haben 511 MB zu missbrauchen, wenn Sie jede Sekunde 4 Bytes mallocieren, ohne sie freizugeben, können Sie fast 73 Stunden lang laufen, bevor Ihnen der Speicher ausgeht. Wenn man bedenkt, dass viele Maschinen einmal am Tag abgeschaltet werden, bedeutet dies, dass Ihrem Programm nie der Speicherplatz ausgeht!

Im obigen Beispiel beträgt das Leck 4 Bytes pro Sekunde oder 240 Bytes/min. Stellen Sie sich nun vor, dass Sie das Byte/Min-Verhältnis verringern. Je niedriger dieses Verhältnis ist, desto länger kann Ihr Programm ohne Probleme laufen. Wenn dein mallocs sind selten, das ist eine reale Möglichkeit.

Verdammt, wenn du weißt, dass du es nur tun wirst malloc etwas einmal, und das malloc nie wieder getroffen werden, dann ist es einer statischen Zuweisung sehr ähnlich, obwohl Sie die Größe dessen, was Sie zuordnen, im Voraus nicht wissen müssen. Beispiel: Sagen wir, wir haben wieder 512 MB. Wir müssen malloc 32 Arrays von ganzen Zahlen. Dies sind typische Ganzzahlen – jeweils 4 Bytes. Wir wissen, dass die Größe dieser Arrays niemals 1024 Ganzzahlen überschreiten wird. In unserem Programm treten keine anderen Speicherzuweisungen auf. Haben wir genug Speicher? 32 * 1024 * 4 = 131.072. 128 KB – also ja. Wir haben viel Platz. Wenn wir wissen, dass wir nie mehr Speicher zuweisen werden, können wir das sicher tun malloc diese Arrays, ohne sie freizugeben. Dies kann jedoch auch bedeuten, dass Sie die Maschine/das Gerät neu starten müssen, wenn Ihr Programm abstürzt. Wenn Sie Ihr Programm 4.096 Mal starten/stoppen, werden Ihnen alle 512 MB zugewiesen. Wenn Sie Zombie-Prozesse haben, ist es möglich, dass der Speicher auch nach einem Absturz nie freigegeben wird.

Erspare dir Schmerz und Elend und konsumiere dieses Mantra als Die Eine Wahrheit: malloc sollte stets verbunden sein mit a free. new sollte stets haben eine delete.

  • Das ist ein guter Punkt. Ich würde jedoch erwarten, dass sie es nicht verwenden würden malloc und so überhaupt in diesem Fall, anstatt sich auf die statische Zuweisung zu verlassen (oder vielleicht einen großen Teil zuzuweisen und dann den Speicher manuell zu handhaben).

    – Luan

    19. März 2014 um 8:38 Uhr

Benutzeravatar von mfro
mfro

Ich denke, die in der Frage angegebene Behauptung ist Unsinn, wenn sie vom Standpunkt des Programmierers wörtlich genommen wird, aber aus Sicht des Betriebssystems ist sie (zumindest teilweise) wahr.

malloc() ruft schließlich entweder mmap() oder sbrk() auf, wodurch eine Seite vom Betriebssystem abgerufen wird.

In jedem nicht-trivialen Programm ist die Wahrscheinlichkeit, dass diese Seite jemals während der Lebensdauer eines Prozesses an das Betriebssystem zurückgegeben wird, sehr gering, selbst wenn Sie den größten Teil des zugewiesenen Speichers freigeben (). Der freie () Speicher wird also die meiste Zeit nur für denselben Prozess verfügbar sein, aber nicht für andere.

1418950cookie-checkIst es jemals in Ordnung, free() für zugewiesenen Speicher *nicht* zu verwenden?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy