6

I am rewriting a set of LED manipulation functions to be more object oriented, and I have found that, mysteriously, my global variable memory space has been over consumed, despite a reduction of the use of global variables.

I played around with a number of things, and found something curious.

Here is a fragment of a function that does not live in an object, and is called directly from loop():

Animation *getNewAnim(int index)
{
    switch(index)
    {
        case 0:
            return new RedClouds();
        case 1:
            return new TwoLayerClouds();
        case 2:
            return new LavaLamp1();
        case 3:
            return new Bubbles1();

        ...  (20 or so more cases)
     }

This is a function that lets me switch between display modes based on the value of index. Only one of these objects is ever created at a given time; I delete the old one before calling this, so there will never be multiple instances in use simultaneously.

Now the curious thing is that if I remove a few of these return statements, collapsing the block into something like this:

Animation *getNewAnim(int index)
{
    switch(index)
    {
        case 0:
        case 1:
        case 2:
        case 3:
            return new Bubbles1();
}

The amount of global memory space used falls considerably.

My hypothesis is that the Arduino build environment is pre-allocating space for each of these objects in the global memory space before the program is even run, rather than doing so dynamically as needed.

Is my hypothesis correct? And if so, is there any way to tell the build environment not to do this, as I'd rather use the same memory for my new object that was used by the old one.

To clarify: When I build I see this sort of output:

Sketch uses 6,550 bytes (21.3%) of program storage space. Maximum is 30,720 bytes.
Global variables use 1,167 bytes (57.0%) of dynamic memory, leaving 881 bytes for local variables. Maximum is 2,048 bytes.

When I refer to "global memory" I am referring to the Global variables number on that output. I'll also note that when I compile with the second example, the program storage space reported also falls dramatically.

NEW HYPOTHESIS

Here is what I think may be going on. When I omit the classes from the switch statement, the compiler seems to optimize them out of the codebase, which is what I expect, and is consistent with seeing the program storage space shrink. Each class has a method that returns it's name. This method contains an embedded string, and I now believe that this string is stored in the global variable space. Therefore, if the class is included, more global variable space is consumed to hold the name, and if it is optimized out, less is consumed. This theory is consistent with all the facts I have, and makes more sense than my previous hypothesis.

  • 1
    Are you referring to PROGMEM or RAM? Please post a Minimal, Complete, and Verifiable example. I don't see how the "build environment" (basically g++) could possibly pre-allocate space for objects which are going to be dynamically allocated anyway. How are you measuring "global memory space", and what do you mean by that precisely? – Nick Gammon Feb 1 '16 at 5:08
  • return new Bubbles1(); - why the brackets? Why not: return new Bubbles1; ? – Nick Gammon Feb 1 '16 at 5:28
  • 1
    Without seeing the rest of your code, but only a snippet it is hard to say. Maybe the other classes have static variables. If the compiler does not ever see them instantiated it might omit the static variables. – Nick Gammon Feb 1 '16 at 6:00
  • 2
    It's theoretical in one sense, but if I can understand why what I'm seeing is happening, I can design around the problem. As it is, I am just guessing at what's going on in the compiler, and, as you say, it doesn't always behave as expected. What I need is a tool to tell me what the compiler is putting into the global variable space. – watusimoto Feb 1 '16 at 6:38
  • 2
    Try running avr-nm -Crtd --size-sort on your elf file. Look for symbols of type 'D' (initialized data), 'B' (BSS) and 'V' (vtable), in either upper or lower case. The list you get is sorted by size. Literal strings may be missing from this list, but you see them with avr-objdump -s -j .data. – Edgar Bonet Feb 1 '16 at 14:56
1

I suggest you take a look at https://stackoverflow.com/questions/16274451/operator-new-for-arduino the new operator is not coming from the c++ libraries, it is coming from the Arduino libraries. It's a wrapper for malloc Is using malloc() and free() a really bad idea on Arduino?

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.