Memory heap

I am new to Arduino programming and I am working on my first program. I have run into the proverbial "Out of SRAM" problem. Can somebody tell me if a variable goes "out of scope" if it frees up SRAM that can be used by other variables?
Thanks!

Arend

Yes, that is correct.

Many times Out of SRAM conditions are caused by string constants used for Serial output. You can avoid having those constants take up SRAM space by using the F() macro:

Seeial.print(F("This string constant stays in FLASH memory rather than being copied to SRAM."));
1 Like

Only some of the variables are lost when they go out of scope. in C/C++ the simple stuff arrays, ints, reals floats and so do disapper
when they go out of scope as they are created on the stack.

However object are created on the heap and need to be expilcitly destroyed before they go out of scope. If you don't when the heap is used up.

Mark

PS there are many other ways to run out of memory why not post your code.

holmes4:
However object are created on the heap and need to be expilcitly destroyed before they go out of scope. If you don't when the heap is used up.

Only if you use new or malloc. While some classes do call those internally (most notably String), most should not.

Can you give me an example of a static object on the Arduino?.

I can have more than 1 serial on the mega so that can't be static. But the pre-processing done by the Arduino's build system makes it hard to tell.

I can have more than one servo or bounce so they can't be and so the list goes on.

Mark

holmes4:
Can you give me an example of a static object on the Arduino?.

I can have more than 1 serial on the mega so that can't be static. But the pre-processing done by the Arduino's build system makes it hard to tell.

I can have more than one servo or bounce so they can't be and so the list goes on.

You can have more than one int on the stack, and you can have more than one Serial on the stack. You don't need to use new if you want to have more than one int, and you don't need it to have more than one Serial or Bounce or Servo.

afuite:
Can somebody tell me if a variable goes "out of scope" if it frees up SRAM that can be used by other variables?

Enough of this vague talk.

Read this before posting a programming question

Post all your code (as an attachment ifnecessary) and describe how you know you are running out of RAM.

@WizenedEE

Not true a simple type is such as int is on the stack, however an instance of a class eg sevro, sreial etc comes in two parts

  1. the data for that instance of the class eg servo1, servo2 which are created on the heap

  2. pointers to the instance data on the heap. The pointers are created on the stack.

You therfore need to free the heap data before the pointer to goes out of scope.

If not the space in the heap remaines allocated but nothing points to it.

Mark

holmes4:

  1. the data for that instance of the class eg servo1, servo2 which are created on the heap

This is true ONLY if you use new or malloc().

Here:
http://www.learncpp.com/cpp-tutorial/79-the-stack-and-the-heap/

learncpp:
In C++, when you use the new operator to allocate memory, this memory is assigned from the heap.

I can't find anything saying that objects go on the stack when they're allocated normally because it's such a silly question.

class myclass {
  public:
    int i;
};

myclass myobject; // on STACK
void setup() {
  myclass myotherobject; // on STACK
} // myotherobject is destroyed

void loop() {
  myclass* myclassptr; // pointer is on STACK
  myclassptr = new myclass; // object is on HEAP --- must be destroyed

  delete myclassptr; // object is no longer on heap
}

This link is also somewhat enlightening:

If you continue to insist that all class objects are created on the heap, you will need to provide references.

(sorry to afuite, this really isn't relevant to your question at all)

holmes4:

  1. the data for that instance of the class eg servo1, servo2 which are created on the heap

  2. pointers to the instance data on the heap. The pointers are created on the stack.

Not necessarily. Both of those would depend on where the data was created.

There is a third piece of memory: global memory, which is where static variables go. The data may well be there.

holmes4:
You therfore need to free the heap data before the pointer to goes out of scope.

Using an object doesn't imply that it owns any allocated memory. It will only own memory if it allocated it, or it was given it. In either case, the usual rules of memory management would apply and when it was destroyed the object would be responsible for releasing any memory that it owned. I think you're implying that objects inherently or commonly use heap memory and that users of those objects need to deal with that specially, and I don't think that's the case.

holmes4:
Can you give me an example of a static object on the Arduino?.

int i_am_static;

void loop() {
  int i_am_not_static;
  static int but_I_am;
}
1 Like

I'm thinking of writing a page on the topic.

There are three places that data, generally speaking, can live: externally to the processor (eg, an SD card), in a register, or in memory.

In the arduino programming environment, memory is used in four main ways: static, heap, stack, and progmem.

etc etc, more words to follow.

On an AVR, registers are part of memory (sort of) and the difference between RAM memory (and registers) and Program Memory is REALLY important (and somewhat difficult to explain.)

Newbie-friendly overview of Arduino memory:

holmes4:
@WizenedEE

Not true a simple type is such as int is on the stack, however an instance of a class eg sevro, sreial etc comes in two parts

  1. the data for that instance of the class eg servo1, servo2 which are created on the heap

  2. pointers to the instance data on the heap. The pointers are created on the stack.

You therfore need to free the heap data before the pointer to goes out of scope.

If not the space in the heap remaines allocated but nothing points to it.

Mark

WRONG. This is only true if you allocate the data that way, by using new. Everything else either has a global lifespan (and is never deleted) or block-level scope, and is automatically deleted when you exit the block. You do not have to manually delete it.

Since you bring up those two classes, here are the class declarations for HardwareSerial and Servo. Please point out to us the "pointers to dynamically allocated memory":

class HardwareSerial : public Stream
{
  protected:
    volatile uint8_t * const _ubrrh;
    volatile uint8_t * const _ubrrl;
    volatile uint8_t * const _ucsra;
    volatile uint8_t * const _ucsrb;
    volatile uint8_t * const _ucsrc;
    volatile uint8_t * const _udr;
    // Has any byte been written to the UART since begin()
    bool _written;

    volatile rx_buffer_index_t _rx_buffer_head;
    volatile rx_buffer_index_t _rx_buffer_tail;
    volatile tx_buffer_index_t _tx_buffer_head;
    volatile tx_buffer_index_t _tx_buffer_tail;

    // Don't put any members after these buffers, since only the first
    // 32 bytes of this struct can be accessed quickly using the ldd
    // instruction.
    unsigned char _rx_buffer[SERIAL_RX_BUFFER_SIZE];
    unsigned char _tx_buffer[SERIAL_TX_BUFFER_SIZE];

  public:
    inline HardwareSerial(
      volatile uint8_t *ubrrh, volatile uint8_t *ubrrl,
      volatile uint8_t *ucsra, volatile uint8_t *ucsrb,
      volatile uint8_t *ucsrc, volatile uint8_t *udr);
    void begin(unsigned long baud) { begin(baud, SERIAL_8N1); }
    void begin(unsigned long, uint8_t);
    void end();
    virtual int available(void);
    virtual int peek(void);
    virtual int read(void);
    int availableForWrite(void);
    virtual void flush(void);
    virtual size_t write(uint8_t);
    inline size_t write(unsigned long n) { return write((uint8_t)n); }
    inline size_t write(long n) { return write((uint8_t)n); }
    inline size_t write(unsigned int n) { return write((uint8_t)n); }
    inline size_t write(int n) { return write((uint8_t)n); }
    using Print::write; // pull in write(str) and write(buf, size) from Print
    operator bool() { return true; }

    // Interrupt handlers - Not intended to be called externally
    inline void _rx_complete_irq(void);
    void _tx_udr_empty_irq(void);
};
class Servo
{
public:
  Servo();
  uint8_t attach(int pin);           // attach the given pin to the next free channel, sets pinMode, returns channel number or 0 if failure
  uint8_t attach(int pin, int min, int max); // as above but also sets min and max values for writes. 
  void detach();
  void write(int value);             // if value is < 200 its treated as an angle, otherwise as pulse width in microseconds 
  void writeMicroseconds(int value); // Write pulse width in microseconds 
  int read();                        // returns current pulse width as an angle between 0 and 180 degrees
  int readMicroseconds();            // returns current pulse width in microseconds for this servo (was read_us() in first release)
  bool attached();                   // return true if this servo is attached, otherwise false 
private:
   uint8_t servoIndex;               // index into the channel data for this servo
   int8_t min;                       // minimum is this value times 4 added to MIN_PULSE_WIDTH    
   int8_t max;                       // maximum is this value times 4 added to MAX_PULSE_WIDTH   
};