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.
return new Bubbles1();
- why the brackets? Why not:return new Bubbles1;
?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 withavr-objdump -s -j .data
.