Ich studiere Computertechnik und habe einige Elektronikkurse. Ich habe gehört, von zwei meiner Professoren (diese Kurse) , dass es möglich ist , das zu vermeiden , mit free()
Funktion (nach malloc()
, calloc()
usw.) , da die Speicherplatz wahrscheinlich zugewiesen wird nicht wieder verwendet werden , um andere Speicher zuweisen. Das heißt, wenn Sie beispielsweise 4 Bytes zuweisen und diese dann freigeben, haben Sie 4 Bytes Speicherplatz, der wahrscheinlich nicht erneut zugewiesen wird: Sie haben ein Loch .
Ich finde das verrückt: Sie können kein Nicht-Spielzeug-Programm haben, in dem 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 es für jeden einen malloc()
geben muss free()
.
Also: Gibt es jemals Umstände, unter denen es angebracht sein könnte, a malloc()
ohne zu verwenden free()
? Und wenn nicht, wie kann ich das meinen Professoren erklären?
Antworten:
Einfach: Lesen Sie einfach die Quelle so ziemlich jeder halb-ernsthaften
malloc()/free()
Implementierung. Damit meine ich den eigentlichen Speichermanager , der die Arbeit der Anrufe erledigt. 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.Es ist sehr üblich, sicherzustellen, dass der Speicher nicht fragmentiert wird, indem benachbarte Löcher zu größeren Löchern zusammengefügt werden. Seriösere Allokatoren verwenden seriösere Techniken, um dies sicherzustellen.
Nehmen wir also an, Sie führen drei Zuweisungen und Aufhebungen durch und erhalten Blöcke, die in dieser Reihenfolge im Speicher angeordnet sind:
Die Größe der einzelnen Zuordnungen spielt keine Rolle. dann befreien Sie den ersten und den letzten, A und C:
Wenn Sie B endgültig befreien, erhalten Sie (zunächst zumindest theoretisch) Folgendes:
was in gerecht zerlegt werden kann
dh ein einzelner größerer freier Block, keine Fragmente mehr übrig.
Referenzen, wie gewünscht:
heap4.c
Code in FreeRTOS .quelle
Die anderen Antworten erklären , bereits sehr gut , dass reale Implementierungen
malloc()
undfree()
tun in der Tat coalesce (defragmnent) Löcher in größeren freien Stücken. Aber selbst wenn dies nicht der Fall wäre, wäre es immer noch eine schlechte Idee, darauf zu verzichtenfree()
.Die Sache ist, dass Ihr Programm gerade diese 4 Bytes Speicher zugewiesen hat (und freigeben möchte). Wenn es über einen längeren Zeitraum ausgeführt wird, ist es sehr wahrscheinlich, dass erneut nur 4 Byte Speicher zugewiesen werden müssen. Selbst wenn diese 4 Bytes niemals zu einem größeren zusammenhängenden Raum verschmelzen, können sie dennoch vom Programm selbst wiederverwendet werden.
quelle
free
es oft genug aufgerufen wird, um die Leistung zu beeinträchtigen, dann wird es wahrscheinlich auch oft genug aufgerufen, so dass das Auslassen eine sehr große Beeinträchtigung des verfügbaren Speichers zur Folge hat. Es ist schwer vorstellbar, dass auf einem eingebetteten System die Leistung ständig darunter leidet,free
abermalloc
nur eine endliche Anzahl von Malen genannt wird. Es ist ein ziemlich seltener Anwendungsfall, ein eingebettetes Gerät zu haben, das Daten einmalig verarbeitet und dann zurücksetzt.Es ist totaler Unsinn, zum Beispiel gibt es viele verschiedene Implementierungen von
malloc
, einige versuchen, den Haufen effizienter zu machen, wie Doug Leas oder dieser .quelle
Arbeiten Ihre Professoren zufällig mit POSIX? Wenn sie es gewohnt sind, viele kleine, minimalistische Shell-Anwendungen zu schreiben, ist dies ein Szenario, in dem ich mir vorstellen kann, dass dieser Ansatz nicht allzu schlecht wäre - das Freigeben des gesamten Heaps auf einmal in der Freizeit des Betriebssystems ist schneller als das Freigeben von a tausend Variablen. Wenn Sie erwarten, dass Ihre Anwendung ein oder zwei Sekunden lang ausgeführt wird, können Sie problemlos ohne Aufhebung der Zuordnung davonkommen.
Es ist natürlich immer noch eine schlechte Praxis (Leistungsverbesserungen sollten immer auf Profilerstellung basieren, nicht auf vagen Bauchgefühlen), und Sie sollten den Schülern nichts sagen, ohne die anderen Einschränkungen zu erklären, aber ich kann mir eine Menge winziger Rohrleitungen vorstellen -Anwendungen, die auf diese Weise geschrieben werden sollen (wenn die statische Zuordnung nicht direkt verwendet wird). Wenn Sie an etwas arbeiten, das davon profitiert, dass Ihre Variablen nicht freigegeben werden, arbeiten Sie entweder unter extrem niedrigen Latenzbedingungen (in diesem Fall, wie können Sie sich überhaupt dynamische Zuweisung und C ++ leisten ?: D), oder Sie sind es etwas sehr, sehr Falsches tun (wie das Zuweisen eines Ganzzahl-Arrays durch Zuweisen von tausend Ganzzahlen nacheinander anstelle eines einzelnen Speicherblocks).
quelle
Sie haben erwähnt, dass sie Elektronikprofessoren sind. Sie können zum Schreiben von Firmware / Echtzeitsoftware verwendet werden, wenn sie in der Lage sind, die Ausführung einer bestimmten Zeit genau zu bestimmen. In diesen Fällen kann es einfacher sein, die Ausführungszeit zu berechnen, wenn Sie wissen, dass Sie über genügend Speicher für alle Zuweisungen verfügen und nicht Speicher freigeben und neu zuweisen.
In einigen Schemata kann der Hardwarespeicherschutz auch verwendet werden, um sicherzustellen, dass die Routine in ihrem zugewiesenen Speicher abgeschlossen ist, oder um in sehr außergewöhnlichen Fällen eine Falle zu erzeugen .
quelle
malloc
und solche in diesem Fall überhaupt nicht verwenden würden , anstatt sich auf die statische Zuordnung zu verlassen (oder vielleicht einen großen Teil zuzuweisen und dann den Speicher manuell zu handhaben).Aus einem anderen Blickwinkel als bei früheren Kommentatoren und Antworten besteht die Möglichkeit, dass Ihre Professoren Erfahrung mit Systemen haben, bei denen der Speicher statisch zugewiesen wurde (dh als das Programm kompiliert wurde).
Die statische Zuordnung erfolgt, wenn Sie Folgendes tun:
In vielen Echtzeit- und eingebetteten Systemen (die am wahrscheinlichsten von EEs oder CEs angetroffen werden) ist es normalerweise vorzuziehen, die dynamische Speicherzuweisung insgesamt zu vermeiden. Also, Verwendungen von
malloc
,new
und ihre Gegenstücke zum Löschen selten. Darüber hinaus ist der Speicher in Computern in den letzten Jahren explodiert.Wenn Ihnen 512 MB zur Verfügung stehen und Sie statisch 1 MB zuweisen, müssen Sie ungefähr 511 MB durchlaufen, bevor Ihre Software explodiert (na ja, nicht genau ... aber gehen Sie hier mit mir). Angenommen, Sie haben 511 MB zu missbrauchen. Wenn Sie 4 Bytes pro Sekunde mallocieren, ohne sie freizugeben, können Sie fast 73 Stunden lang laufen, bevor Ihnen der Speicher ausgeht. Wenn man bedenkt, dass viele Computer einmal am Tag ausgeschaltet werden, bedeutet dies, dass Ihrem Programm niemals der Speicher ausgeht!
Im obigen Beispiel beträgt das Leck 4 Bytes pro Sekunde oder 240 Bytes / min. Stellen Sie sich nun vor, Sie senken das Byte / Min-Verhältnis. Je niedriger dieses Verhältnis, desto länger kann Ihr Programm problemlos ausgeführt werden. Wenn Ihre
malloc
s selten sind, ist das eine echte Möglichkeit.Wenn Sie wissen, dass Sie nur
malloc
einmal zu etwas gehen und dasmalloc
nie wieder getroffen wird, ähnelt dies einer statischen Zuordnung, obwohl Sie nicht wissen müssen, wie groß das ist, was Sie zuweisen. Vorderseite. Beispiel: Nehmen wir an, wir haben wieder 512 MB. Wir müssenmalloc
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 weiteren Speicherzuordnungen auf. Haben wir genug Speicher? 32 * 1024 * 4 = 131.072. 128 KB - also ja. Wir haben viel Platz. Wenn wir wissen, dass wir niemals mehr Speicher zuweisen werden, können wir dies sicher tunmalloc
diese Arrays, ohne sie zu befreien. Dies kann jedoch auch bedeuten, dass Sie den Computer neu starten müssen, wenn Ihr Programm abstürzt. Wenn Sie Ihr Programm 4.096 Mal starten / stoppen, weisen Sie alle 512 MB zu. Wenn Sie Zombie-Prozesse haben, ist es möglich, dass Speicher auch nach einem Absturz nie freigegeben wird.Sparen Sie sich Schmerz und Elend und konsumieren Sie dieses Mantra als Die Eine Wahrheit:
malloc
sollte immer mit einem assoziiert werdenfree
.new
sollte immer eine habendelete
.quelle
sizeof(int)
gleich 2 statt 4.Ich denke, die in der Frage angegebene Behauptung ist Unsinn, wenn sie vom Standpunkt des Programmierers aus wörtlich genommen wird, aber sie hat aus Sicht des Betriebssystems die Wahrheit (zumindest einige).
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 während einer Prozesslebensdauer jemals an das Betriebssystem zurückgegeben wird, sehr gering, selbst wenn Sie den größten Teil des zugewiesenen Speichers freigeben (). Der freie Speicher von () würde also die meiste Zeit nur für denselben Prozess verfügbar sein, nicht jedoch für andere.
quelle
Ihre Professoren sind nicht falsch, aber auch falsch (sie sind zumindest irreführend oder zu stark vereinfacht). Die Speicherfragmentierung führt zu Problemen bei der Leistung und der effizienten Nutzung des Speichers. Manchmal müssen Sie dies berücksichtigen und Maßnahmen ergreifen, um dies zu vermeiden. Ein klassischer Trick besteht darin, dass Sie, wenn Sie viele Dinge mit derselben Größe zuweisen, beim Start einen Speicherpool abrufen, der ein Vielfaches dieser Größe beträgt, und dessen Verwendung vollständig intern verwalten, um sicherzustellen, dass beim Fragmentieren keine Fragmentierung auftritt Betriebssystemebene (und die Löcher in Ihrem internen Speicher-Mapper haben genau die richtige Größe für das nächste Objekt dieses Typs).
Es gibt ganze Bibliotheken von Drittanbietern, die nichts anderes tun, als solche Dinge für Sie zu erledigen, und manchmal ist es der Unterschied zwischen akzeptabler Leistung und etwas, das viel zu langsam läuft.
malloc()
undfree()
nehmen Sie sich merklich Zeit für die Ausführung, was Sie bemerken werden, wenn Sie sie häufig anrufen.Also nur um naiv zu vermeiden verwenden
malloc()
undfree()
Sie können sowohl die Fragmentierung und Leistungsprobleme vermeiden - aber wenn Sie rechts unten , um es zu bekommen, sollten Sie immer sicherstellen , dass Siefree()
alles , was Sie ,malloc()
wenn Sie einen sehr guten Grund, es anders zu machen. Selbst wenn ein interner Speicherpool verwendet wird, wird der Poolspeicher von einer guten Anwendungfree()
vor dem Beenden verwendet. Ja, das Betriebssystem wird es bereinigen, aber wenn der Anwendungslebenszyklus später geändert wird, kann man leicht vergessen, dass der Pool immer noch herumhängt ...Langzeitanwendungen müssen natürlich äußerst gewissenhaft sein, wenn es darum geht, alles, was sie zugewiesen haben, zu bereinigen oder zu recyceln, da ihnen sonst der Speicherplatz ausgeht.
quelle
Ihre Professoren sprechen einen wichtigen Punkt an. Leider ist der englische Sprachgebrauch so, dass ich nicht ganz sicher bin, was sie gesagt haben. Lassen Sie mich die Frage in Bezug auf Nicht-Spielzeug-Programme beantworten, die bestimmte Eigenschaften der Speichernutzung aufweisen und mit denen ich persönlich gearbeitet habe.
Einige Programme verhalten sich gut. Sie ordnen Speicher in Wellen zu: viele kleine oder mittlere Zuordnungen, gefolgt von vielen Freigaben, in sich wiederholenden Zyklen. In diesen Programmen sind typische Speicherzuordnungen ziemlich gut. Sie verschmelzen freigegebene Blöcke und am Ende einer Welle befindet sich der größte Teil des freien Speichers in großen zusammenhängenden Blöcken. Diese Programme sind ziemlich selten.
Die meisten Programme verhalten sich schlecht. Sie weisen den Speicher mehr oder weniger zufällig in einer Vielzahl von Größen von sehr klein bis sehr groß zu und geben ihn frei, und sie behalten eine hohe Verwendung der zugewiesenen Blöcke bei. In diesen Programmen ist die Fähigkeit zum Zusammenführen von Blöcken begrenzt und im Laufe der Zeit endet der Speicher stark fragmentiert und relativ nicht zusammenhängend. Wenn die Gesamtspeicherauslastung in einem 32-Bit-Speicherbereich etwa 1,5 GB überschreitet und Zuweisungen von (z. B.) 10 MB oder mehr vorhanden sind, schlägt möglicherweise eine der großen Zuweisungen fehl. Diese Programme sind üblich.
Andere Programme geben wenig oder gar keinen Speicher frei, bis sie aufhören. Sie weisen während der Ausführung nach und nach Speicher zu, geben nur kleine Mengen frei und stoppen dann. Zu diesem Zeitpunkt wird der gesamte Speicher freigegeben. Ein Compiler ist so. So ist eine VM. Beispielsweise gibt die in C ++ selbst geschriebene .NET CLR-Laufzeit wahrscheinlich nie Speicher frei. Warum sollte es?
Und das ist die endgültige Antwort. In den Fällen, in denen das Programm über ausreichend Speicher verfügt, ist die Verwaltung des Speichers mit malloc und free keine ausreichende Antwort auf das Problem. Sofern Sie nicht das Glück haben, mit einem gut erzogenen Programm zu arbeiten, müssen Sie einen oder mehrere benutzerdefinierte Speicherzuweiser entwerfen, die große Speicherblöcke vorab zuweisen und dann gemäß einer Strategie Ihrer Wahl unterzuweisen. Sie dürfen free überhaupt nicht verwenden, außer wenn das Programm stoppt.
Ohne genau zu wissen, was Ihre Professoren gesagt haben, würde ich für Programme im Produktionsmaßstab wahrscheinlich auf ihrer Seite stehen.
BEARBEITEN
Ich werde versuchen, einige der Kritikpunkte zu beantworten. Offensichtlich ist SO kein guter Ort für Beiträge dieser Art. Um es klar zu sagen: Ich habe ungefähr 30 Jahre Erfahrung im Schreiben dieser Art von Software, einschließlich einiger Compiler. Ich habe keine akademischen Referenzen, nur meine eigenen blauen Flecken. Ich kann nicht anders, als zu spüren, dass die Kritik von Menschen mit weitaus engeren und kürzeren Erfahrungen kommt.
Ich werde meine Kernbotschaft wiederholen: Das Ausbalancieren von Malloc und Free ist keine ausreichende Lösung für die Speicherzuweisung in großem Maßstab in realen Programmen. Block Coalescing ist normal und kostet Zeit, reicht aber nicht aus. Sie benötigen seriöse, clevere Speicherzuordnungen, die dazu neigen, Speicher in Blöcken (mit Malloc oder was auch immer) zu erfassen und selten freizugeben. Dies ist wahrscheinlich die Botschaft, die die Professoren von OP im Sinn hatten und die er missverstanden hat.
quelle
Ich bin überrascht, dass noch niemand das Buch zitiert hat:
http://sarabander.github.io/sicp/html/5_002e3.xhtml#FOOT298
In der Tat können viele Programme problemlos funktionieren, ohne sich jemals die Mühe zu machen, Speicherplatz freizugeben.
quelle
Ich kenne einen Fall, in dem das explizite Freigeben von Speicher schlimmer als nutzlos ist . Das heißt, wenn Sie alle Ihre Daten bis zum Ende der Prozesslebensdauer benötigen . Mit anderen Worten, wenn sie freigegeben werden, ist dies nur unmittelbar vor Beendigung des Programms möglich. Da jedes moderne Betriebssystem darauf achtet, Speicherplatz freizugeben, wenn ein Programm stirbt, ist ein Aufruf
free()
in diesem Fall nicht erforderlich. Tatsächlich kann dies die Programmbeendigung verlangsamen, da möglicherweise auf mehrere Seiten im Speicher zugegriffen werden muss.quelle