Reducing SRAM usage by limiting scope of objects.

RayLivingston: freeMemory would certainly NOT count anything that lives on the stack. Maximum stack size is allocated at compile time, and does not change as the program runs.

Regards, Ray L.

avr-gcc doesn't know what the maximum stack usage for any given program is; that would require dynamic stack usage analysis.

So the stack isn't allocated like the heap is.

But I was just wondering if freeMemory looked at the current stack size, but it probably doesn't, which makes it useless to determine memory usage when objects are allocated only on the stack.

RayLivingston: freeMemory would certainly NOT count anything that lives on the stack. Maximum stack size is allocated at compile time, and does not change as the program runs.

Regards, Ray L.

You may want to use Andy Brown's memory library.

The usable stack size would change during execution as the heap grows towards it. A snippet from the library mentioned above:

size_t getFreeMemory()
{
  return (size_t)AVR_STACK_POINTER_REG-
         (size_t)__malloc_margin-
         (size_t)__malloc_heap_start-
         getMemoryUsed();
}

By using the stack pointer register we know where the stack is now, and hence how much is left "below" it.

Shouldn't we recover the SRAM used for instantiating the object foo after it goes out of scope?

By not showing how foo is declared this is just a guessing game. Does it allocate memory dynamically? Does it free it? I'm not going to keep trying to answer questions about "why does my hidden code do something?".

RayLivingston:
freeMemory would certainly NOT count anything that lives on the stack. Maximum stack size is allocated at compile time, and does not change as the program runs.

Regards,
Ray L.

Thanks, this is starting to make more sense. I’m no expert, but it seems like the ultimate goal is to keep your stack and heap from colliding. I’m just trying to figure out techniques beyond the obvious F() and PROGMEM to prevent this and test to see if it has happened or is close.

I’ve tried to get a feeling for how this all works, but I keep seeing references to new(), vector and malloc() with very conflicting advice as to if they should be used.

Here’s an example which seems to put the object on the heap or the stack:

extern unsigned int __heap_start;
extern void *__brkval;

/*
 * The free list structure as maintained by the 
 * avr-libc memory allocation routines.
 */
struct __freelist {
  size_t sz;
  struct __freelist *nx;
};

/* The head of the free list structure */
extern struct __freelist *__flp;

/* Calculates the size of the free list */
int freeListSize() {
  struct __freelist* current;
  int total = 0;
  for (current = __flp; current; current = current->nx) {
    total += 2; /* Add two bytes for the memory block's header  */
    total += (int) current->sz;
  }
  return total;
}

int freeMemory() {
  int free_memory;
  if ((int)__brkval == 0) {
    free_memory = ((int)&free_memory) - ((int)&__heap_start);
  } else {
    free_memory = ((int)&free_memory) - ((int)__brkval);
    free_memory += freeListSize();
  }
  return free_memory;
}

class memUserStack {
    public:
      char stuff[2000];
};

class memUserHeap{
  public:
    char * stuff;
    memUserHeap(){
      stuff = new char[2000];
      for ( uint16_t i ; i < 2000 ; i++ ) stuff[i] = 'A';
    }
    ~memUserHeap() {delete[] stuff;}
};


void setup(){
  uint16_t i = 0;
  pinMode(2,INPUT);
  digitalWrite(2,LOW);
  Serial.begin(57600);
  Serial.print(F("Memory before we intantiate anything: "));
  Serial.println(freeMemory());
  if ( digitalRead(2) == 0 ) {
    memUserHeap foo;
    //for ( i = 0; i < 2000 + 1 ; i++ ) foo.stuff[i] = 42;
    Serial.print(F("Memory after we intantiate foo in Heap: "));
    Serial.println(freeMemory());
    memUserStack bar; bar.stuff[1] = 'B';
    Serial.print(F("Memory after we intantiate bar in Stack: "));
    Serial.println(freeMemory());
  }
  Serial.print(F("Memory after foo and bar go out of scope: "));
  Serial.println(freeMemory());
}

void loop(){
  Serial.print(F("Memory when loop begins(): "));
  Serial.println(freeMemory());
  while (true){};
}

and the output:

Memory before we intantiate anything: 7463
Memory after we intantiate foo in Heap: 5461
Memory after we intantiate bar in Stack: 5461
Memory after foo and bar go out of scope: 7463
Memory when loop begins(): 7465

So it looks like freeMem only measures heap. memUserHeap and memUserStack will use the same amount of ram, and when we get to loop() it’s a wash.

I’m not saying that memUserHeap is better, probably the contrary, just trying to find a clear example of how memory works and can be optimized.

What I’m not sure of is if you were short of memory and needed to juggle it in setup() or elsewhere, what the best way would be to:

Instantiate large object.
Do something with object.
Delete object and recover ram
repeat with another object.

As an example, rather than make the SD card a global persistent object using a big chunk of RAM, could I instantiate an SD object right before I need to read or write, then destroy the SD object after I’m done. Since it is destroyed shortly after it is instantiated, there shouldn’t be a hole left in heap (?). I could see that this might be slow, but I’m not really concerned with speed.

RyanN:    Serial.println(foo.stuff[10]); // So the compiler doesn't optimize this away

Why would that line of code inhibit dead-code removal?

RayLivingston: freeMemory would certainly NOT count anything that lives on the stack.

The version in reply #16 does...

  int free_memory;
    ...((int)&free_memory)...

RyanN: So it looks like freeMem only measures heap.

For the version in reply #16 that is not correct.

I have posted two complete self-contained code examples hopefully illustrating the question of where does the RAM come from when an object is instantiated and when do you get it back. [quote author=Coding Badly date=1423424336 link=msg=2082282] Why would that line of code inhibit dead-code removal? [/quote] It seems to me, and I very well may be wrong, that if you instantiate an object foo and never do anything with it before it goes out of scope, the compiler will optimize it away (and warn you: sketch_feb07a.ino:67: warning: unused variable 'foo'). I think I've seen this behavior. Again I very well may be wrong, but it can't hurt. This is why I did if ( digitalRead(2) == 0 )instead ofif (true) since the compiler won't know ahead of time what will happen. I wish I knew more about how the compiler optimizes the code.

[quote author=Nick Gammon date=1423423657 link=msg=2082265] You may want to use Andy Brown's memory library. [/quote] I fiddled with it for a while, but I can't seem to get it working in the Arduino environment. I wonder if anyone has ported this to an Arduino compatible library.

Local variables are ALWAYS allocated on the stack. Any objects you create with new will get allocated on the heap. Any variables, objects or code that you create in your source code, then never actually use, will be optimized out by the compiler. I don't know the specifics on Arduino, but very often compilers try to estimate maximum stack need,by looking at things like local variables, function recursion and nesting, etc. and reserve that much stack space at compile time, with the rest of free RAM going to the heap. If you use a lot of heap, and do a lot of recursion, that's when you can run the stack and heap into each other, and things get ugly.

Regards, Ray L.

RyanN: I fiddled with it for a while, but I can't seem to get it working in the Arduino environment. I wonder if anyone has ported this to an Arduino compatible library.

It's an Arduino library, so I don't understand that.

Sorry, the library is fine, it's the example of how to use it I can't get to work.

What did you try? What happened? “I can’t get to work” could mean anything.