Wie funktioniert eigentlich realloc()? Sucht das Betriebssystem(?) quasi bei einer Anfrage nach (zB.) Vergrößerung des Speichers einen neuen zusammenhängenden Speicherbereich? D.h, ich bekäme nach jedem: addr = realloc(str, strlen(buffer)+new_len+1); eine neue Adresse? Und falls ja, was passiert dann mit dem alten Speicher? Dieser müsste ja dann automatisch freigegeben werden, oder?
Falls realloc != 0/NULL returniert, dann kann es ein Pointer zu einem anderen Speicherbereich sein (in dem Fall wird der alte Pointer mit free freigegeben), oder aber der selbe, kommt drauf an wies gemacht ist.
Reallokierer schrieb: > Wie funktioniert eigentlich realloc()? > Sucht das Betriebssystem(?) quasi bei einer Anfrage nach (zB.) > Vergrößerung des Speichers einen neuen zusammenhängenden > Speicherbereich? D.h, ich bekäme nach jedem: > addr = realloc(str, strlen(buffer)+new_len+1); > eine neue Adresse? Ja, darauf das sich addr bei jedem realloc() ändert muss Deine Software vorbereitet sein. Aber immer passiert das nicht. Reallokierer schrieb: > Und falls ja, was passiert dann mit dem alten Speicher? Dieser müsste ja > dann automatisch freigegeben werden, oder? Der wird von der Speicherverwaltung recycelt und steht dann wieder als freier Speicher für alloc() zur Verfügung.
Reallokierer schrieb: > Sucht das Betriebssystem(?) Nicht das Betriebssystem, sondern die Heapverwaltung der Laufzeitumgebung Deines Compilers.
Rufus Τ. Firefly schrieb: > Nicht das Betriebssystem, sondern die Heapverwaltung der > Laufzeitumgebung Deines Compilers. ... die dann für eine kräftige Fragmentierung sorgen können.
holger schrieb: > ... die dann für eine kräftige Fragmentierung sorgen können. Nach der gar keiner gefragt hatte. Ich kann diese ewige „malloc ist per se böse“-Leier einfach nicht mehr erhören. Zum TE: der Variantenreichtum der Implementierungen ist dabei extrem groß. Das ist auch eine Frage, ob du auf einem System mit virtuellem Speicher arbeitest (bei dem man recht großzügig sein kann) oder es eine eher kleine Umgebung mit chronischer Speicherknappheit ist.
holger schrieb: > ... die dann für eine kräftige Fragmentierung sorgen können. Dafür braucht es kein realloc, das geht mit malloc, calloc und free genausogut.
Autor: Rufus Τ. Firefly (rufus) (Moderator) schrieb: >Nicht das Betriebssystem, sondern die Heapverwaltung der >Laufzeitumgebung Deines Compilers. Dasch a ja interessant. Also kwasi so ne Art "Middleware" zwischen OS und Anwendung. 73
Rufus Τ. Firefly schrieb: > ... das geht mit malloc, calloc und free genausogut. auf diesem Weg macht es realloc @Jörg Wunsch deswegen: > ... die dann für eine kräftige Fragmentierung sorgen können. achte auf das "sorgen können". Eine > „malloc ist per se böse“-Leier kann Ich in der Zeile nicht erkennen. Und ja, der TE hatte nicht danach gefragt. Etwas Hintergrund kann aber nicht schaden.
holger schrieb: > Etwas Hintergrund kann aber nicht schaden. Dann präzisiere das doch. Denn auf einem halbwegs modernen PC wirst du schon ziemlich viel tun müssen bis du da etwas merkst.
holger schrieb: > auf diesem Weg macht es realloc Aha, du weißt es ja genau. Hast du denn selbst schon mal eins geschrieben oder dir angesehen, wie eins funktioniert, welches andere geschrieben haben? Man kann es sicher auf diesem Wege machen, aber das ist eher ein Notnagel denn eine vernünftige Implementierung. Reallokierer schrieb: > Also quasi so ne Art "Middleware" zwischen OS und Anwendung. Ja, nennt sich Standardbibliothek. OS muss übrigens nicht sein.
Autor: Jörg Wunsch (dl8dtl) (Moderator)
> OS muss übrigens nicht sein.
Stimmt, jetzt wo du's wieder sagst... Für den Arduino Due mit SAM3X8E
mit 64 + 32 Kbytes RAM steht der Befehl zur verfügung.
Was meinen die Experten? den Befehl verwenden? Oder eher Finger
weglassen?
73
Reallokierer schrieb: > Autor: Jörg Wunsch (dl8dtl) (Moderator) >> OS muss übrigens nicht sein. > Stimmt, jetzt wo du's wieder sagst... Für den Arduino Due mit SAM3X8E > mit 64 + 32 Kbytes RAM steht der Befehl zur verfügung. > > Was meinen die Experten? den Befehl verwenden? Oder eher Finger > weglassen? Das kann man pauschal nicht sagen. Da Du aber auf eine Prozessor mit 64kB + 32kB RAM verweist (der möglicherweise nicht insgesamt von malloc, realloc verwaltetet wird), kann man hinzufügen, da0 die allgmeine Tendenz eher dahin geht, malloc/realloc bei sowenig (soviel) RAM zu vermeiden. Es kommt auch auf den Verwendungszweck an. Wenn ein Programm relativ viele kleine Blöcke alloziiert ist das auf einem PC mit Gigabytes eher akzeptabel, als auf einem uC mit einigen Kilobyte. Du musst bedenken, dass die Speicherverwaltung selbst Speicher benötigt. Einige (soweit mir bekannt die allermeisten) Speicher-Verwaltungen benötigen einige Bytes pro angefordertem Block (genauer: zusammenhängendem Speicherbereich). Bei wenig Speicher und Anforderungen von Bereichen die nicht wesentlich über dem Overhead liegen, wird das schnell ineffizient.
kommt drauf an... Man muß sich halt bewusst sein, welche Resourcen man hat, wieviel man braucht und was man sinnvollerweise macht, wenn es eng wird. Wenn man den Überblick nicht hat (warum auch immer), dann besser die Finger davon lassen. Das ist wie mit gutem (scharfem) Werkzeug. Wenn man nicht damit umgehen dann, dann lieber nicht anfassen.
Reallokierer schrieb: > den Befehl verwenden? Wenn du ein dynamisches Problem hast, wirst du mit einer statisch vorallozierten Implementierung kaum besser kommen als mit malloc(), denn du riskierst, dass deine Implementierung Bugs mit sich bringt, die aus dem „offiziellen“ malloc() alle schon rausgeschüttelt sind. Du solltest dir natürlich genau ansehen, auf welche Weise man das mit der Newlib daherkommende (und durch deine selbst zu erstellende _sbrk()-Backend-Funktion zu untersetzende) malloc() „auf die sichere Seite“ bekommt, sprich, das Risiko einer Kollision von Heap und Stack möglichst gering zu bekommen. Eine Anwendung mit dynamischem Datenanfall muss immer für die Situation gerüstet sein, dass sie mehr Daten bekommen könnte, als sie Speicher zur Verfügung hat, und muss daraus einen Ausweg haben. Das ist komplett unabhängig davon, ob man die Verwaltung einem malloc() überlässt oder statisch vorbelegt (bei letzterem wird der Fall sogar mit großer Wahrscheinlichkeit schon eher eintreten). Wenn du ein rein statisch lösbares Problem hast, stellt sich die Frage eigentlich gar nicht. ;-)
:
Bearbeitet durch Moderator
Man könnte noch folgende "Daumenregeln" hinzufügen: 1. Auf einem uC ist der Speicherbedarf bei den allermeisten Anwendungen von vorneherein bekannt oder es kann ein Maximalbedarf benannt werden. Dann würde man eher ein Array definieren. 2. Falls dieses Array nicht per se wie ein Ringpuffer oder Kellerspeicher bedient wird, schreibt man sich eine eigene kleine Verwaltung für die Einträge (ein paar Zeiger oder/und ein Flag in einer Struktur die dann in dem Array gespeichert wird). 3. Falls man aber viele solcher Arrays mit kleinen Bereichen für die Speicherung der Anwendungsdaten braucht dann gibt es einen Grenzwert in dem die eigene Verwaltung genauso viel Overhead erzeugt wie die von malloc/realloc. Dann kann man 2. ebensogut sein lassen. 4. Auf einem uC allerdings wird man einigen Aufwand in Vorüberlegungen stecken um malloc/realloc zu vermeiden. 5. Auf einem PC hingegen ist der Speicherbedarf oft nicht bekannt. Insbesondere die Abfolge der nötigen Anforderungen ist oft nicht bekannt (im gegensätzlichen Fall könnte man einige logisch getrennte Variablen zusammenlegen, da sie immer zeitlich und kausal zusammenhängend gespeichert werden müssen). 6. Auf einem PC ist Speicher meist im Überschuss vorhanden, so dass die Verwendung von malloc/realloc meist garnicht in Frage steht. 7. Allerdings gibt es auch Anwendungen für den PC die den Speicher voll ausnutzen (z.B. Bild- oder Videobearbeitung) und in denen Swapping aus Zeitgründen vermieden werden soll. Dann greifen die unter 3. bzw. 5. genannten Maßnahmen.
Bitflüsterer schrieb: > 2. Falls dieses Array nicht per se wie ein Ringpuffer oder > Kellerspeicher bedient wird, schreibt man sich eine eigene kleine > Verwaltung für die Einträge (ein paar Zeiger oder/und ein Flag in einer > Struktur die dann in dem Array gespeichert wird). Dann bist du bei dem Punkt, den ich oben nannte: das Risiko, da Bugs einzubringen, ist deutlich größer, als wenn man gleich malloc() nimmt. Letztlich ist malloc() auf einem Controller auch weiter nichts als die Verwaltung eines großen Byte-Arrays, welches sich zwischen dem Ende der statischen Daten und dem Stack befindet. Beim malloc() aus der (hier relevanten) Newlib kann man es sogar vollständig so nutzen, da man dort die Backend-Funktion _sbrk() ohnehin selbst liefern muss, die den nächsten freien Speicherblock anfordert. Aber auch beim statischen Array als „Heap“ hat man das Risiko, wenn man dieses zu groß macht, dass da der Stack hineinrennt … Nur, dass da eben kein malloc() mehr eine 0 zurückgeben kann.
Ich möchte sowohl Overhead minimieren, als auch die Fragmentierung des Speichers verhindern. Ich habe mir vorgestellt wie ich selbst "realloc" implementieren würde: falls neue Länge kleiner als jetzige ist, dann nur das Delta am Ende freigeben und Fertig! Nun meine Fragen: Wenn es garantiert ist, dass in der Zukunft die Größe/Länge des allokierten Speichers gleich bleibt oder kleiner wird, 1. bleibt die Anfangs-Adresse gleich? (ISO, C99 od. Sonstiges?) 2. werden die nicht mehr benötigten Bytes ("Delta") am Ende freigegeben?
1 | /*****************************************
|
2 | Am Anfang (nach "setup()" Aufruf.)
|
3 | Addr. ---------------------
|
4 | 0x0815 |0|1|2|3|4|5|6|7|8|9|
|
5 | ---------------------
|
6 | |
7 | Nach aufruf von "realloc(5, sizeof(uint8_t))" ?
|
8 | Addr. ---------------------
|
9 | 0x???? |0|1|2|3|4|freigeg.?|
|
10 | ---------------------
|
11 | ******************************************/
|
12 | |
13 | #define MAX_SIZE 10
|
14 | |
15 | uint8_t* ptr; |
16 | |
17 | void setup() |
18 | {
|
19 | *ptr = (uint8_t*)calloc(MAX_SIZE, sizeof(uint8_t)); |
20 | }
|
21 | |
22 | void loop() |
23 | {
|
24 | // Tue dies..., tue das...
|
25 | |
26 | newLength = 5 ; // Neue Länge ist garantiert kleiner als MAX_SIZE. |
27 | |
28 | // Nun brauchen wir weniger Speicher.
|
29 | *ptr = (uint8_t*)realloc(newLength, sizeof(uint8_t)); |
30 | }
|
Otto Realverbraucher schrieb: > Wenn es garantiert ist, dass in der Zukunft die Größe/Länge des > allokierten Speichers gleich bleibt oder kleiner wird, > 1. bleibt die Anfangs-Adresse gleich? (ISO, C99 od. Sonstiges?) > 2. werden die nicht mehr benötigten Bytes ("Delta") am Ende freigegeben? Nichts davon ist garantiert. Was die avr-libc-Implementierung betrifft, handhabt sie das im Prinzip so, aber den Sourcecode kannst du dir ja selbst ansehen.
:
Bearbeitet durch Moderator
Jörg W. schrieb: > Nichts davon ist garantiert. > > Was die avr-libc-Implementierung betrifft, handhabt sie das im Prinzip > so, aber den Sourcecode kannst du dir ja selbst ansehen. Vielen Dank Jörg W.! Um zu garantieren, dass die anderen globale Variablen im Heap, alle nach dem allokierten Bereich platziert werden, bringt es was den allokierten Array als Erstes zu definieren und Speicherplatz zuweisen (statisch)? z.B.: uinit8_t * ptr = (uint8_t*)malloc(MAX_SIZE * sizeof(uint8_t)); bool b = false; char c = 'c'; int16_t i = 0; ... usw.
Ich denke nicht, dass solche Verrenkungen irgendwelcher Art wirklich was bringen. Entweder hast du ein Problem, welches sich mit statisch belegtem Speicher erledigen lässt, d. h. alle Details sind zur Compilezeit bekannt. Dann brauchst du kein malloc(). Oder du hast wirklich dynamische Daten, weißt zur Compilezeit also nicht, was wirklich wann auf dich zukommt. Dann brauchst du 1.) immer (egal ob malloc() oder nicht) eine „Exit-Strategie“, also irgendeine Maßnahme, die du dann ergreifen kannst, wenn die Datenflut größer wird, als deine Speichermöglichkeiten es hergeben. 2.) brauchst du dann einen Allokator, bspw. eben malloc(). Bei wirklich zufälligen Anforderungen an die Größe der jeweiligen Speicherblöcke ist die Gefahr von Fragmentierung nicht so groß, wie das gemeinhin angenommen wird. Sofern überhaupt noch ein paar Reserven da sind, gleicht sich das über die Programmlaufzeit wieder aus. Deine Variablen „ptr“, „b“, „c“ und „i“ spielen dabei nur insofern eine Rolle, als dass sie beim Eintritt in die Funktion Stack belegen könnten (oft genug wird sie der Compiler ohnehin in Registern halten). Der für den Stack benutzte Platz steht dem Heap nicht mehr zur Verfügung, der ist also schon weg, bevor dein malloc() da überhaupt aufgerufen wird.
:
Bearbeitet durch Moderator
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.