3

I have a few Arduino sketches that take 70-90% of dynamic memory. It seems that over 70%, the compiler gives the warning:

Low memory available, stability problems may occur.

but otherwise I wasn't noticing any problems.

Then I uploaded a sketch that took 89%, which logged String data to the serial port, and found that only newlines were being written out. When I commented out some global variables so that dynamic memory usage got to 88%, then it started printing out the data.

Is there any hard/fast rule on when this problem occurs? I was surprised by this problem, because the global variables I removed weren't even being used in the sketch, so I had thought the compiler would have removed or optimized them so they didn't waste any space. Are there any ways to automatically improve compilation efficiency and detect wasteful variables?

3

The typical build memory statistics is:

Global variables use XXX bytes (X%) of dynamic memory, 
leaving YYY bytes for local variables. 
Maximum is ZZZ bytes.

What is all this about? First, the maximum number of bytes SRAM depends on the MCU. Arduino Uno is ATmega328 based and has 2K byte SRAM. Arduino Mega2560 has 8K byte. "Dynamic memory" in the print out is SRAM. It is not the heap (malloc/free). To avoid confusion I will use "data memory" below.

The compiler allocates memory for all global variable (objects, struct, etc). It also allocates memory for literal strings (character vectors). These are copied from program memory to data memory at startup. All static variables in functions are also allocated. The sum of all these allocations are the "Global variables".

To execute the sketch data memory is used for the stack. It contains the function parameters, local variables in called functions (that are not allocated to registers), saved registers such as return address to the caller, etc. The size of the frame is known by the compiler and may be found in the assembly listing of a sketch. The maximum stack size required is tricky to calculate as it is the call chain with the largest frame size sum. This is not necessarily the deepest call chain. Also the compiler does to a fair amount of inlining.

The heap (malloc/free) is used by classes such as String. The heap starts directly after the global variables in data memory and grows towards the stack. The heap is blocks of memory allocated and deallocated during the execution of the sketch. This behavior may give heap fragmentation.

Interrupt Service Routines (ISR) need to be able to execute "on top of" any function call. The ISRs are typically called on hardware events such as USART data received. The compiler has to generate code that stores the machine state on stack to execute the ISR. Depending on how many registers the ISR needs more or less of the machine state (registers) need to be stored. The worst case is all 32 registers.

The warning at 30% of SRAM is at approx. 600 bytes left (Arduino Uno, 2K byte). That has to cover the stack and possible heap usage together with ISRs. With a stack heap limit of 32-128 byte, ISR frame of 32-64 byte, that actually leaves worst case approx. 400 bytes for local variables in functions and possible usage of the heap (such as String).

Cheers!

1

The problems occur when you run out of memory. That all depends on what you are doing with the memory that is available.

String objects are not good and are wasteful of memory. They really should be avoided, since they use dynamic memory allocation, which uses the "spare" memory that is what's left over from what the compiler says is used.

There is no rule, other than "don't run out of memory", and tracking dynamic usage can be very hard, so try to avoid using it if you can.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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