5

I wanted to check how much memory (RAM) is used by an "empty" sketch. Here is my code:

int freeRam() 
{
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

void setup()
{
  Serial.begin(9600);
  Serial.println(freeRam());
}

void loop()
{
}

My result was that I have 1839 bytes of free RAM. I have an Arduino UNO, so there are 2048 bytes of RAM in total. Which means that 209 bytes are already taken. Wait, 209 bytes?! I started thinking and figured out that the Serial buffer must take some space. I've opened HardwareSerial.h and found this:

#if (RAMEND < 1000)
  #define SERIAL_BUFFER_SIZE 16
#else
  #define SERIAL_BUFFER_SIZE 64
#endif

I've changed SERIAL_BUFFER_SIZE to 16 to see what will happen. (I've tried decreasing it to 1, but then the serial communication didn't work properly and I wasn't able to display the amount of free RAM). After this modification my result is 1935. So now "only" 113 bytes are taken. But what is taking this memory? HardwareSerial has 2 buffers:

struct ring_buffer
{
  unsigned char buffer[SERIAL_BUFFER_SIZE];
  volatile unsigned int head;
  volatile unsigned int tail;
};

ring_buffer rx_buffer = { { 0 }, 0, 0};
ring_buffer tx_buffer = { { 0 }, 0, 0};

So when the SERIAL_BUFFER_SIZE is 16 they take exactly 40 bytes. What is taking the remaining bytes (113-40= 73 bytes)? I'm aware that stack frames take some memory - calling main() which calls setup() whichs calls freeRam() probably take a few bytes, but 73 still seems too much.

2
  • what version of arduino are you compiling this on?
    – BrettAM
    Jan 12, 2015 at 20:32
  • my version is 1.0.6 Jan 13, 2015 at 6:54

2 Answers 2

11

First off, a sketch that uses Serial cannot really be considered an empty sketch as Serial drags along a lot of function definitions, at least one Serial class instance (more on Arduino Mega, for instance), plus probably some more global variables...

sizeof Serial may be interesting to display as Serial is not limited to the TX and RX buffers, as you seem to believe. Its class also has a couple of additional data members on its own, plus members inherited from its superclasses, Stream and Print.

In addition, the Arduino IDE comes with the various "utilities" which you normally can't get rid of, like the millisecond timer that uses at least 9 bytes (see reference in wiring.c file).

So in the end, even an empty sketch (even without Serial) takes some SRAM space for global variables.

I think there is some way, with avr-size, to check out, with fine details, the size of data (not accounting for the runtime stack) after the sketch has been fully built.

Arduino IDE already displays some of this static memory information (at the end of "verification").

For example, on my IDE (1.5.8) for Arduino UNO, a really empty sketch, once compiled is mentioned to use 9 bytes of SRAM. Actually those 9 bytes are for 3 global variables, used by the millisecond timer. This number, of course, does not account for runtime memory usage, in particular the stack.

Then, just adding Serial.begin(9600); makes it use 182 bytes (ie 173 more bytes than an empty sketch). So you get the culprit.

9

I investigated this at some length a while back. On this page about putting constant data into program memory I obsessively tracked down every last byte of memory used in a small sketch. This was the sketch:

#include "memdebug.h"

void setup ()
  {
  Serial.begin (115200);
  Serial.println ();
  Serial.print (F("Free memory = "));
  Serial.println (getFreeMemory ());
  }  // end of setup

void loop () { } 

memdebug.h came from Andy Brown - Debugging AVR dynamic memory allocation.

That particular sketch actually used even more RAM:

Free memory = 1702

So where did the 346 bytes go? Here:

  • 34 bytes for the HardwareSerial instance (Serial)

  • 64 bytes for the Serial transmit buffer

  • 64 bytes for the Serial receive buffer

  • 4 bytes for the Serial transmit buffer head and tail pointers

  • 4 bytes for the Serial receive buffer head and tail pointers

  • 9 bytes for keeping track of millis / micros

  • 4 bytes for memory allocation (__malloc_heap_start, __malloc_margin)

  • 128 bytes for the heap safety margin

  • 6 bytes for a few nested function calls (main -> setup -> getFreeMemory)

  • 16 bytes for the compiler vtable for HardwareSerial

  • 4 bytes for variables __brkval and __flp (used in memdebug)

  • 2 bytes pushed onto the stack in main (to save registers)

  • 2 bytes pushed onto the stack in setup (to save registers)

  • 4 bytes pushed onto the stack in getFreeMemory (to save registers)

  • 1 byte because the stack pointer starts at 0x8FF rather than 0x900

    (That's 346 bytes accounted for)

2
  • What's a heap safety margin? Jul 7, 2015 at 21:44
  • The heap and the stack grow towards each other (in opposite directions, one up, one down). Without a safety margin it would be possible to allocate room on the heap, with little or no "spare", and then crash soon afterwards when you call a function. Calling functions uses the stack, which would then encroach into the heap. Effectively the heap would get corrupted, if the stack didn't first (for example if you wrote to the memory in the heap, in the function you called). The heap safety margin gives a buffer zone to (try to) prevent that.
    – Nick Gammon
    Jul 7, 2015 at 21:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.