Forum: Mikrocontroller und Digitale Elektronik Avr: Objekte, C++ und Speicherverwaltung


von Jörg (Gast)


Lesenswert?

Hallo,

ich habe da ein kleines Problem, welches ich nicht ganz nachvollziehen 
kann.

Ich habe eine Klasse für einen Ringspeicher geschrieben, welche 256 
Bytes Buffer nutzt (jedes Objekt). Ich nutze den Atxmega16a4.

Meine Programmausführung ist davon abhängig, wie groß ich diesen Buffer 
anlege und kommt ggf. ganz zum stehen. Selbst wenn ich 10 dieser 
Ringspeicher anlege und über eine Inputvariable diese Ringspeicher 
beschreibe, legt der Compiler nur ein paar Bytes im Speicher an.

Es müssten doch deutlich mehr sein, soviele Register gibt es doch nicht, 
dass diese 2560 Bytes kommplett in diesen gehalten werden.

Es scheint dann eine  magische Grenze zu geben, ab wo das Programm 
wieder reibungslos läuft. Wieso wird so wenig Speicher angelegt, obwohl 
es 2560 Bytes sein sollten?

Übersehe ich eine Projekteinstellung?

Danke schonmal :) !

von Peter II (Gast)


Lesenswert?

Jörg schrieb:
> Es scheint dann eine  magische Grenze zu geben, ab wo das Programm
> wieder reibungslos läuft. Wieso wird so wenig Speicher angelegt, obwohl
> es 2560 Bytes sein sollten?

kannst du uns auch etwas code zeigen?

Wenn der speicher mit New/Malloc angelegt wird, wirst du es nicht sehen.

von Rene H. (Gast)


Lesenswert?

Zeig mal den Code.

von Ulrich F. (Gast)


Lesenswert?

Mein Tipp:
Verzichte auf die dynamische Speicherverwaltung.
Lege diese Objekte statisch an. Von mir aus global in einem Array.
Dann zeigt dir der Compiler den korrekten Verbrauch und die Gefahr von 
Heap/Stack Kollisionen sinkt erheblich.

von Stefan F. (Gast)


Lesenswert?

Der Compiler belegt nur Platz für statische Variablen. Objekte werden 
hingegen in der Regel dynamisch (mit new) angelegt (was nicht heissen 
soll, dass es keine statischen Objekte gibt).

Wenn deine Objekte zur Laufzeit vom Code erzeugt werden, dann kannst du 
am Output des Compilers nicht mehr ablesen, wieviel RAM das Programm 
wirklich benötigt.

War Dir das soweit klar?

von Jörg (Gast)


Angehängte Dateien:

Lesenswert?

Klar mach ich. Natürlich sind alle Objekte bereits statisch angelegt - 
sofern ich den Begriff hier richtig verstanden habe. Kein new, delete 
oder malloc.

Avr Studio 7.
1
#include <avr/io.h>
2
#include "c_ringbuffer.h"
3
#include "c_avr_iopin.h"
4
5
int main(void)
6
{
7
  c_ringbuffer ringe[12];
8
  c_avr_iopin led(&PORTD, PIN7_bm, true, false);
9
  int i;
10
11
    while (1) 
12
    {
13
    led.toggle();
14
    
15
    for(i = 0; i < 12; i++)
16
      ringe[12].addItem(i);
17
    }
18
}

von Jörg (Gast)


Lesenswert?

PS: Hier wird 0 Bytes im Ram angelegt.

von Mark B. (markbrandis)


Lesenswert?

Ulrich F. schrieb:
> Lege diese Objekte statisch an. Von mir aus global in einem Array.

Naja, globale Objekte sind aus softwaretechnischer Sicht eher hässlich.

von Mark B. (markbrandis)


Lesenswert?

Jörg schrieb:
>
1
>     for(i = 0; i < 12; i++)
2
>       ringe[12].addItem(i);
3
>

Du schreibst hier über die Grenze eines Arrays hinaus. Kein Wunder dass 
da was schiefläuft.

von Jörg (Gast)


Lesenswert?

Mark B. schrieb:
> Jörg schrieb:
>>>     for(i = 0; i < 12; i++)
>>       ringe[12].addItem(i);
>>
> Du schreibst hier über die Grenze eines Arrays hinaus. Kein Wunder dass
> da was schiefläuft.

Ich habe den Code kurz reduziert und zusammengestellt, damit ich das 
hier veranschaulichen kann. Tatsächlich ein Fehler, aber das behebt das 
Problem nicht.

von Jörg (Gast)


Lesenswert?

Mark B. schrieb:
> Ulrich F. schrieb:
>> Lege diese Objekte statisch an. Von mir aus global in einem Array.
>
> Naja, globale Objekte sind aus softwaretechnischer Sicht eher hässlich.

In meinem gezeigten Beispiel kommt die led bereits zum stehen aber 3 
Bytes im Ringspeicher :(.

von Ulrich F. (Gast)


Lesenswert?

Mark B. schrieb:
> Naja, globale Objekte sind aus softwaretechnischer Sicht eher hässlich.

Zu 100% richtig!
Aber so werden sie vom Compiler gesehen und der Verbrauch richtig 
angezeigt.
Wenigstens testweise kann man das ja mal tun...

Jörg schrieb:
> int main(void)
> {
>   c_ringbuffer ringe[12];

So werden die Objekte auf dem Stack angelegt, und der Verbrauch ist 
nicht zu sehen.

3,3K Ram minus 12  mal 256Byte Buffer ( plus Wasserkopf)
Da bleibt nicht mehr viel...

von Stefan F. (Gast)


Lesenswert?

<code>
int main(void)
{
  c_ringbuffer ringe[12];
}
</code>

Ich bin da jetzt nicht 100% sicher, aber immerhin 90%. Und zwar meine 
ich, dass die Variable c_ringbuffer hier auch erst zur Lautzeit angelegt 
wird, allerdings auf dem Stack. Denn es handelt sich um eine lokale 
Variable innerhalb der main Funktion.

Versuche mal, sie global außerhalb der main Funktion zu verschieben oder 
schreibe "static" davor.

von Jörg (Gast)


Lesenswert?

Stefan U. schrieb:
> <code>
> int main(void)
> {
>   c_ringbuffer ringe[12];
> }
> </code>
>
> Ich bin da jetzt nicht 100% sicher, aber immerhin 90%. Und zwar meine
> ich, dass die Variable c_ringbuffer hier auch erst zur Lautzeit angelegt
> wird, allerdings auf dem Stack. Denn es handelt sich um eine lokale
> Variable innerhalb der main Funktion.
>
> Versuche mal, sie global außerhalb der main Funktion zu verschieben oder
> schreibe "static" davor.

Tatsächlich, also globale Objekte zeigt mir dieser nun 99,9% an, wie 
erwartet.

von Cpp (Gast)


Lesenswert?

Hallo Jörg,

irgend wer hat hier im Forum mal diese Funktion zur Anzeige des freien 
Ramspeichers vorgeschlagen:
1
#ifdef ARDUINO_UNO
2
int freeRam() {
3
  extern int __heap_start, *__brkval;
4
  int v;
5
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
6
}
7
#endif

Mit der Funktion kannst Du zur Laufzeit sehen, wie viel HEAP noch übrig 
ist und wie viel für weitere Objekte verbleibt.
Ich benutze sie zum zyklischen Speichercheck.

von Stefan F. (Gast)


Lesenswert?

Der Haken an dieser Methode ist, dass der Heap fragmentiert werden kann. 
Solange man sich nicht außerordentlich darum bemüht, dies zu verhindern, 
wird das auch der Fall sein.

Dann stellt sich die Frage: Was ist freier Speicher?

a) Der größe verfügbare zusämmenhängende Block, oder
b) Alle freien Blöcke zusammen gezählt?

Genau genommen muss man für jeden einzelnen Bedarfsfall (also jede 
einzelne Funktion, Methode und jeden Aufruf von new, malloc, calloc 
sowie die Library Funktionen, die Heap benötigen, wie z.B. printf) 
prüfen, ob noch genügen freier Speicher vorhanden ist.

von Ulrich F. (Gast)


Lesenswert?

Cpp schrieb:
> Mit der Funktion kannst Du zur Laufzeit sehen, wie viel HEAP noch übrig
> ist

Ein wirklicher Schutz vor Stack/Heap Kollisionen ist das auch nicht.

von Cpp (Gast)


Lesenswert?

>Genau genommen muss man für jeden einzelnen Bedarfsfall (also jede
>einzelne Funktion, Methode und jeden Aufruf von new, malloc, calloc
>sowie die Library Funktionen, die Heap benötigen, wie z.B. printf)
>prüfen, ob noch genügen freier Speicher vorhanden ist.

So mache ich es quasi für mein Debugging: Ich gebe den Heap vor und nach 
der Initialisierung aus, damit ich abschätzen kann, wie lange es noch 
reichen wird.
Bei meinem Programm sollten 100Byte Reserve reichen.

von Stefan F. (Gast)


Lesenswert?

Ich hatte bisher nur eine Einzige Anwendung, wo der Speicher knapp 
wurde. Da habe ich dafür gesorgt, dass auf Knopfdruck eine 
Wiederholschleife gestartet wird, die den freien Speicher testet. Etwa 
so (pseudo code):
1
int size=4096;
2
while (1) 
3
{
4
  if (kann size byte allokieren) 
5
  {
6
    den gerade allokierten Speicher wieder freigeben;
7
    printf("Größer freier Speicherblock = %d bytes",size);
8
    break;
9
  }
10
  else
11
  {
12
    if (size<100)
13
    {
14
      printf("Speicher ist voll!");
15
      break;
16
    }
17
    size -= (size/8);
18
  }
19
}

100 Bytes brauche ich mindestens, sonst stürzt das Programm ab. Diesen 
Grenzwert habe ich experimentiell herausgefunden.

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
Noch kein Account? Hier anmelden.