Hi,
Angenommen ich reserviere einen Speicherbereich von 10 Bytes mithilfe
der Funktion "malloc" und möchte ihn nach einer gewissen Zeit kürzen z.B
auf 5 Bytes ohne dass die ersten 5 Bytes verloren.
Ist es möglich dass mit der Funktion "realloc" den Speicherbereich auf 5
zu reduzieren wobei die oberen 5 Bytes wieder freigegeben werden und
ohne dass die ersten 5 Bytes verändert / gelöscht werden?
Denn in meinem Programm wird zyklisch (alle 4 Sekunden) eine Struktur
(Task) von insgesamt 25 Bytes im Speicherbereich (Warteschlange)
hinzugefügt. Diese Struktur hat eine ID und wird von von einem
Taskmanager bearbeitet und schlussendlich gelöscht, wenn der Task
beendet wurde. Beim löschen des Tasks wird es eben mit der Funktion
"realloc" realisiert doch nach ca. 30 erfolgreichen Betriebminuten gibt
mit die realloc (Beim hinzufügen einer Struktur) einen NULL-Pointer
zurück (-> Speicher voll bzw. kein Speicherbereich gefunden der gross
genug ist).
Deshalb vermute ich, dass der Speicher mit "realloc" (Size < aktuelle
Size) den überschüssigen Speicherbereich nicht freigibt.
Ich hoffe ihr könnt mir einige Ratschläge / Tipps geben.
Freundliche Grüsse
Rob
rob schrieb:> Deshalb vermute ich, dass der Speicher mit "realloc" (Size < aktuelle> Size) den überschüssigen Speicherbereich nicht freigibt.
Hast Du Dich schon mal mit Speicherfragmentierung beschäftigt?
rob schrieb:> Angenommen ich reserviere einen Speicherbereich von 10 Bytes mithilfe> der Funktion "malloc" und möchte ihn nach einer gewissen Zeit kürzen z.B> auf 5 Bytes ohne dass die ersten 5 Bytes verloren.> Ist es möglich dass mit der Funktion "realloc" den Speicherbereich auf 5> zu reduzieren wobei die oberen 5 Bytes wieder freigegeben werden und> ohne dass die ersten 5 Bytes verändert / gelöscht werden?
Ja, das ist möglich. Ob realloc den Speicher wieder freigibt, hängt von
dessen Implementation ab. Aber auch wenn es ihn freigibt, wird der freie
Speicher ja nicht einfach in einen großen Eimer gekippt, aus dem man
sich nachher bedienen kann. Du hast, wenn du deinen 10-Byte-Block auf 5
Byte reduzierst, dann eben einen freien Block von 5 Bytes. Das nächste
malloc(10) kann diesen nicht nutzen, da er zu klein ist. Irgendwann ist
dann dein Speicher voll, obwohl noch viel Platz ist, weil du überall
verteilt einzelne freie Bereiche hast, von denen aber jeder zu klein
ist, um ein neues Objekt zu erzeugen. Das nennt sich
Speicherfragmentierung.
das deine erste Version nicht richtig sein kann, ersiehst du schon
daran, dass TaskList.Task nicht per Pointer übergeben wird. realloc hat
daher gar keine Chance, da den neuen Pointer Wert reinzuschreiben. D.h.
dein realloc reallokiert immer mit demselben Pointer, selbst wenn der
Speicher bereits längst freigegeben wurde. Das geht also nur solange
gut, solange realloc den allokierten Speicher nicht verschieben muss.
Was normalerweise nur dann sauber möglich ist, wenn ansonsten keine
dynamische Allokierungen passieren. Wenn das aber das einzige ist, was
dynamisch allokiert wird, sollte man sich fragen, wie sinnvoll eine
dynamische Allokierung überhaupt ist.
An deiner Stelle würde ich mir auch überlegen, ob die Sicht der Dinge,
das alles als 1 großes Array anzusehen, vernünftig ist. Je großer die zu
allokierende Speichermenge ist, desto schwieriger wird es für die
Allokierung einen geeigneten Speicherblock bei fragmentiertem Speicher
zu besorgen.
Rufus Τ. Firefly schrieb:> rob schrieb:>> Deshalb vermute ich, dass der Speicher mit "realloc" (Size < aktuelle>> Size) den überschüssigen Speicherbereich nicht freigibt.>> Hast Du Dich schon mal mit Speicherfragmentierung beschäftigt?
Ehrlich gesagt habe ich mich damit nicht intensiv beschäftigt, ich weiss
zwar schwammig was das ist, doch ich meine das hat beim AVR Xmega keinen
Einfluss aufs RAM indem die Strukturen ja angelegt werden.
Ich werde mich jedenfalls damit beschäftigen, man lernt nie aus!
Aber ich glaube ich hab das Problem gefunden:
Falls ich den letzten aktiven Task lösche will die Delete funktion den
Speicherbereich mit "realloc"(size !!0!!) "kürzen". Vielleicht gelingt
das ja auch doch sobald wieder ein Task hinzugefügt wird, und wenn er
der ERSTE ist, wird mit malloc, nicht realloc, einen neuen
Speicherbereich definiert, obwohl der vorherige zwar mit realloc( size 0
) gekürzt wurde, jedoch nicht komplett freigegeben wurde(Annahme).
so muss in der Delete Funktion beim löschen eines Tasks zwischen
folgenden Situationen unterschieden werden:
Anzahl Tasks > 1 -> muss dann "realloc" verwenden
und
Anzahl Tasks <= 1 -> muss dann "free" verwenden.
Ich teste diese Korrektur mal, wär jedoch schön wenns jemand bestätigen
könnte :)
Rob
rob schrieb:> Ehrlich gesagt habe ich mich damit nicht intensiv beschäftigt, ich weiss> zwar schwammig was das ist, doch ich meine das hat beim AVR Xmega keinen> Einfluss aufs RAM indem die Strukturen ja angelegt werden.
Wie meinst du das denn? Natürlich hat es Einfluss und zwar in dem es
deinen RAM fragmentiert. Und sehr bald darauf wird malloc dann eine
false zurückliefern. Wehe dem der dann nicht das 6. Gebot beachtet ;-)
gruß cyblord
rob schrieb:> Aber ich glaube ich hab das Problem gefunden:> Falls ich den letzten aktiven Task lösche will die Delete funktion den> Speicherbereich mit "realloc"(size !!0!!) "kürzen".
Das passt schon.
Man darf realloc mit 0 aufrufen. Das wirkt dann wie ein free und du
kriegst von realloc einen NULL Pointer zurück. Wenn du den auch noch in
den Task Pointer schreiben würdest, wäre so gesehen die Sache geritzt.
Du musst schon nach den Regeln spielen. realloc wird IMMER nach dem
Muster benutzt
ptr = ralloc( ptr, new_size );
IMMER!
Egal ob realloc dann sein Bestes tut um Speicherverschiebungen zu
vermeiden. Wenn du Annahmen darüber triffst, wie die Speicherverwaltung
intern arbeitet, hast du gerade deine Seele an den Teufel verkauft.
Hallo Karl Heinz Buchegger
Du hast wohl einen fatalen Fehler gefunden, vielen Dank.
Generell müssen immer ca 2-3 Strukturen à 25 Bytes allokiert sein, aber
es muss auch möglich sein, dass eine "grosse" (10-20?) Anzahl in die
"Warteschlange" gespeichert werden kann, welche dann Parallel abarbeitet
wird.
Die Frage ob es sinnvoll ist das dynamisch zu regeln frag ich mich auch
grad. Doch wenns so funktioniert wäre es doch nichts schlechtes?
(Sicherlich besser als statisch?)
Grüsse Rob
Rolf Magnus schrieb:> Du hast, wenn du deinen 10-Byte-Block auf 5> Byte reduzierst, dann eben einen freien Block von 5 Bytes.
Real noch weniger: es geht ja noch Overhead für die Verwaltung der
Blöcke mit drauf, selbst bei einem kleinen Speichermodell (AVR) sind
das noch 2 Bytes. Es bliebe also ein Block übrig, aus dem man 3
Bytes neu allozieren könnte. Erst, wenn auch der ursprüngliche Block
wieder freigegeben ist, kann man beide freien Blöcke wieder zu einem
zusammenfügen, der wieder 10 Bytes hergeben würde (aber natürlich
nur, wenn der hintere Block nicht mittlerweile doch durch eine
Allokation von 3 Bytes noch belegt worden war).
Werden die restlichen Blöcke wirklich nie mehr freigegeben, dann
wird der Speicher ziemlich schnell fragmentieren auf diese Weise.
rob schrieb:> Die Frage ob es sinnvoll ist das dynamisch zu regeln frag ich mich auch> grad. Doch wenns so funktioniert wäre es doch nichts schlechtes?> (Sicherlich besser als statisch?)
Falls irgendwie möglich, ist statisch besser. Dann weißt du nämlich im
vorraus wieviel Speicher du brauchst. Dynamischer Speicher ist auf
kleinen Controllern (und der xmega gehlört da noch dazu) nicht so prall.
gruß cyblord
rob schrieb:> Generell müssen immer ca 2-3 Strukturen à 25 Bytes allokiert sein, aber> es muss auch möglich sein, dass eine "grosse" (10-20?) Anzahl in die> "Warteschlange" gespeichert werden kann, welche dann Parallel abarbeitet> wird.> Die Frage ob es sinnvoll ist das dynamisch zu regeln frag ich mich auch> grad. Doch wenns so funktioniert wäre es doch nichts schlechtes?> (Sicherlich besser als statisch?)
Das Problem:
Du hast ja nicht mehr Speicher zur Verfügung, nur weil du dynamisch
allokierst. Wenn Ende der Fahnenstange ist, dann ist Ende.
Ob daher der Speicher brach liegt, bist du ihn mittels alloc allokierst,
oder ob du genau denselben Speicher in ein vordimensioniertes Array
steckst, kommt sich letzten Endes aufs gleiche raus - wenn das das
einzige ist, was du dynamisch allokierst.
Einziger UNterschied: bei statischer Allokierung taucht der
Speicherverbrauch in der COmpilerstatistik über den Speicherverbrauch
auf und du kannst abschätzen bzw. entscheiden ob du mit dem Heap
kollidieren wirst oder nicht.
Bei dynamischer Allokierung musst du ..... auf dein Glück vertrauen,
dass genau letzteres nicht passiert.
Karl Heinz Buchegger schrieb:> realloc wird IMMER nach dem> Muster benutzt>> ptr = realloc( ptr, new_size );
Naja. Diese Form ist nur garantiert, wenn new_size wirklich kleiner
wird als die originale Größe.
Will man mit realloc einen Block vergrößern, muss man einen zweiten
Zeiger zu Hilfe nehmen:
1
new_ptr=realloc(ptr,new_size);
2
if(new_ptr==NULL){
3
throw_exception(OUT_OF_MEMORY);
4
returnptr;// ptr zeigt noch auf den alten Bereich und ist nach
>returnptr;// ptr zeigt noch auf den alten Bereich und ist nach
5
>// wie vor gültig; ggf. noch free() aufrufen!
6
>}
7
>ptr=new_ptr;
8
>
Ja, ist mir auch gerade geschossen, als ich den Beitrag weggegeben habe,
dass man von realloc ja auch einen NULL Pointer im Fehlerfall kriegt.
Danke, für die Klarstellung.
Karl Heinz Buchegger schrieb:> Ob daher der Speicher brach liegt, bist du ihn mittels alloc allokierst,> oder ob du genau denselben Speicher in ein vordimensioniertes Array> steckst, kommt sich letzten Endes aufs gleiche raus - wenn das das> einzige ist, was du dynamisch allokierst.
Hatten wir neulich schon mal. Bei einer (zu) großen statischen
Allokierung riskiert man immer, dass man mit dem Stack kollidiert,
ohne dass das noch jemand zur Laufzeit bemerkt. Bei einer dynamischen
Zuweisung könnte es zumindest prinzipiell noch bemerkt werden, dass
der Abstand zum Stack jetzt zu knapp wird.
Jörg Wunsch schrieb:> Hatten wir neulich schon mal. Bei einer (zu) großen statischen> Allokierung riskiert man immer, dass man mit dem Stack kollidiert,> ohne dass das noch jemand zur Laufzeit bemerkt.
Deswegen gibts da ja auch die Speicherstatistik, mit der man zumindest
ein Gefühl dafür kriegt, wieviel Platz noch für den Stack übrig ist. Mit
der Kentniss des Programmaufbaus kann man dann wenigstens Daumen mal Pi
abschätzen, ob das reichen könnte oder nicht.
> Bei einer dynamischen> Zuweisung könnte es zumindest prinzipiell noch bemerkt werden, dass> der Abstand zum Stack jetzt zu knapp wird.
Es sei denn du hast den Fall, dass dir der Stack in den allokierten
Speicher rein wächst. Dann kann alles mögliche passieren, wenn Daten
geschrieben werden und danach ein Rücksprung aus einer Funktion
passiert, deren Returnadresse nicht mehr stimmt.
cyblord ---- schrieb:> Wird das denn von malloc und co. auch tatächlich bemerkt? Ist das> entsprechend implementiert?
In der avr-libc ist es so implementiert. Die Reserve, die zwischen
Stack und Heap noch frei bleiben muss beim Allozieren, ist durch
den Nutzer einstellbar (Variable __malloc_margin, default 32 Bytes).
Karl Heinz Buchegger schrieb:> Deswegen gibts da ja auch die Speicherstatistik, mit der man zumindest> ein Gefühl dafür kriegt, wieviel Platz noch für den Stack übrig ist. Mit> der Kentniss des Programmaufbaus kann man dann wenigstens Daumen mal Pi> abschätzen, ob das reichen könnte oder nicht.
Eine vergleichbare Abschätzung kann man aber auch in einem dynamischen
Projekt machen, nur eben nicht gleich nach dem Compilieren ;), sondern
während der Entwicklungsphase, nachdem die Applikation einen typischen
Zeitraum bereits lief. Es ist hie wie da ein "Bauchgefühl", das man
da ansetzt, aber mehr kann man den Umständen entsprechend sowieso nie
tun: wenn der Speicher alle ist, ist einfach Schluss. Die Appliakation
kann nicht noch schnell beim Controller-Hersteller welchen nachkaufen
gehen. ;-)
Albert schrieb:> Hallo Ich muss das Thema wieder aufrollen.
Mach das bitte in einem neuen, eigenen Thread anstelle einen fünf Jahre
alten, in dem es um andere Dinge geht.
Oh, und wenn Du das machst, nutze die [ c ] [ /c ] -Tags, die das Forum
anbietet, damit man Deinen Quelltext vernünftig lesen kann.