Main Site Documentation

mBuino/RETRO - class instances and memory


#1

OK, as someone clearly spoiled by the long use of managed languages, I’m looking for some pointers (pun intended) on best practices for creating and managing class instances.

Here’s a simple example of what I’m doing now, which results in the eventual crashing of my program (or that’s what it looks like, anyway):


class MyGameObject {
    public:
        MyGameObject();
        
        int x;
        int y;
};

MyGameObject::MyGameObject(){
    this->x = rand() % 160;
    this->y = rand() % 128;
}

class MyGame {

    // private vars
    DisplayN18 disp;
    vector<MyGameObject *> objs;
    static const int MAX_OBJS = 20;
  
    // private functions
    void init();

    // public interfaces
    public:
        MyGame();
        
        void tick();
};

MyGame::MyGame(){
    this->init();    
}

void MyGame::init() {
    disp.clear();
    MyGameObject *obj;
    this->disp.clear();
    for (int i = 0; i < MyGame::MAX_OBJS; i++) {
       obj = new MyGameObject;
       objs.push_back(obj);
       disp.fillRect(obj->x, obj->y, 2, 2, DisplayN18::WHITE);
       wait_ms(50);
    }
}

void MyGame::tick() {
    MyGameObject *obj;
    obj = new MyGameObject;
    // erase oldest object by drawing over it with black
    disp.fillRect(objs.front()->x, objs.front()->y, 2, 2, DisplayN18::BLACK);
    // remove oldest object from vector
    objs.erase(objs.begin());
    // add new object to vector
    objs.push_back(obj);
    // draw new object
    disp.fillRect(obj->x, obj->y, 2, 2, DisplayN18::WHITE);
    wait_ms(10);
}

int main() {    
    MyGame game;

    while (true)
        game.tick();
}

What I’m trying to figure out is whether the technique(s) I’m currently using are apt to lead to memory leaks, and if so, how to correct them. Or perhaps I’m having issues because of how I’m drawing items on the display?

The symptoms I’ve seen are:
[ul]In some cases (usually if I’m including text on screen for debugging), the display stops updating, and I get a small rectangle made up of purple and green lines in the upper left corner of the display. When using a build that does this, it stops updating consistently in the same spot on every run.
In other cases, the objects continue to be created and erased for a while, and at some point, no new objects are created (or if they’re created, they’re not drawn on the display) but the old ones are erased, until the display is completely blank. Short of adding USBSerial for debugging (which I may not even have enough room for), I have no way to tell if the program is still running at this point (though I suppose I could set up one of the LEDs to blink on/off every tick as a means of testing that).[/ul]

As I’ve noted elsewhere, I’m very new to C++, and much more used to C#, VB, and JavaScript, so I’m faced with trying to be productive with a very sharp knife that takes time to learn. Any advice or recommendations are welcome.

EDIT: I should note that I arrived at the code above through trial and error, working from the example program that ships with RETRO as a starting point, looking up unfamiliar stuff on http://www.cplusplus.com/ and the mbed site, and fixing compiler errors as I find them. So it’s entirely possible that the code above, while it may compile, may be complete crap. Feel free to say so…my feelings won’t be hurt. :slight_smile:


#2

@ devhammer - Every tick you are creating a new instance of MyGameObject, there is no GC so each time you end-up consuming more memory. In C/C++ you would need to explicitly delete the instances you create. Even though you are calling ‘erase’ on the vector, that only clears out the vector, it does not delete the instances it clears from the vector.

Try see if making the following change helps


// remove oldest object from vector
    delete (*objs.begin());         // Reclaim the memory for the object
    objs.erase(objs.begin());     // Remove the object from the vector

Having said that, I would like to offer the following advice. Avoid dynamic memory allocation if you can. Even if you free the instances, if you are dynamically allocating and freeing objects on the fly and those objects are of differing sizes you will eventually run out of memory due to heap fragmentation.

Another general rule I would recommend, is to always use the simplest data structure that will do the job, if you know you will have a max of 20 objects then allocate a static array of 20 objects and then just track the live objects in the array. The really leads on from the first point, you want to try avoid dynamic memory allocations.


#3

I think @ taylotza got most of the things I was going to say.

An array of MyGameObject * and then a counter holding the index of the oldest one would be a lot more efficient than a vector where you constantly remove and add objects.
If you want to use a vector rather than an array then in your initialization use


to allocate the memory in one go rather than 20 small chunks.

If possible each tick modify the existing MyGameObject rather than deleting it and creating a new one, that will be a lot faster. You could even have an array of objects rather than pointers in that situation and eliminate all dynamic memory functions. How practical this approach is depends a lot on the complexity of the objects.


Having said that, other than the missing delete what you are doing is perfectly good c++ and would be my preferred method of doing this on a desktop, it's a cleaner more flexible method. Unfortunately it is also slower and uses up more memory, both things that you don't have much of on this CPU.
If you plan on making the system more complex then it may make sense to keep things the way they are and hope you have space but as general rule avoid dynamic memory allocation whenever possible.

#4

@ taylorza & @ AndyA - Thanks to you both for your responses. Very much in line with what I thought I might be doing wrong…part of my problem is not knowing how to get rid of objects, which I now know. :slight_smile:

Though as Andy mentioned, it probably makes more sense to modify the object’s properties rather than delete and re-create it. Also glad to hear that my code wasn’t completely terrible, albeit not optimized for the memory capacity of the device.

I’ll make some of the suggested changes and let you know how it works out.

Thanks again!


#5

First results are in…just adding the delete statement makes a HUGE difference.

And it turns out that the program was not crashing per se, at least not in the second scenario where the objects stopped being created. I verified this by simply adding a an LED.write(true), wait, LED.write(false) to the tick function…when the screen goes blank (this is without the delete statement, mind you), the LED continues to blink, so I know that tick is still being called. So I’m not sure what’s actually happening, but in any event, adding delete has it working quite well, even with the dynamic allocation in the vector. Been running for several minutes now, with no indication of issues.

So I may stick with this for the moment, or at least until I run into further issues, at which point I may revisit.

Do either of you happen to know if there is an internal property or object for mbed that has the currently available memory? That might be useful to print out to the screen so I can keep track when debugging.


#6

If you haven’t already, this is an important read: https://developer.mbed.org/handbook/Memory-Model
Post number #2 by Clark Martin ( https://developer.mbed.org/forum/helloworld/topic/3040/ ), is the method I use when trying to debug memory issues.

Another thing to note, is that if your stack runs down over your heap, I don’t think you get any kind of exception… It may do nothing (no objects allocated at top of heap, completely crash, or run with unexplained execution.

For mbuino, the current heap size is 3072 bytes. When debugging memory issues, I always get a pointer to a stack based object (local/automatic variable) at the beginning of main (the system takes some stack before calling main). Then I find someway to indicate stack depth (character LCD, serialUSB to PC with teraterm, even do it with LEDs, by lighting up different ones). As the code executes down call chains… use these methods to monitor the stack.

Then to monitor the heap, after any dynamic object/array allocation, you can take the address of the base and add-in the size of the object/array.

Old school and tedious but you don’t have many other options other than taking shots in the dark via code modifications.


#7

No easy way that I know of to get the amount of free space as a number to display. And since the free memory may not all be in one block a single number could be misleading.

You can use:



This outputs text along the lines of:
[quote]
alloc block 1000034c size  18
alloc block 10000364 size fc8
free block  1000132c size   c next=00000000
------- heap validation complete[/quote]
to stderr (which is the physical serial port on pins P0_18 and P0_19).

No reason the output has to go to standard error but you do need to give a function that operates like fprintf and the destination parameter for that function.
Presumably you could use sprintf and pass it a large enough buffer that it won't overrun. You could then parse the response for lines starting with "free block".

Slow, messy and probably not worth if for what you are doing but in theory possible.


I seem to remember someone on mbed posting that there is a 4 byte overhead for each block of dynamic memory.

#8
  1. I didn’t go back and look but I’m pretty sure that the mbed memory link i posted describes heap management in terms of growth and the action of delete.

  2. Free space is just bottom_end_stack minus address-of-end-of-last-block in heap.

bottom stack approx = starting stack in main less 3K (get address of a local variable in main for your guess at top of stack)

end of heap: in main use malloc or new to get a small object take the return pointer and add the sizeof(memory-just allocated). I seem to recall there is no heap management that attempts to use dis-contiguous blocks of free space. For that, many of the suggestions here would be worked and/or craft-your-own heap management (free, alloc, new, …) and initializing it at start-up with all free heap (I’m sure there are lots of memory managers on the web, some may be simple enough to use with embedded constraints).

Buyer beware: these are all approximations, given the limited amount of total memory.


#9

@ Jeff - Thanks. I’ll look into using the technique you described if I need to figure out what’s happening with memory again.

As it stands, I let the new version (still using vector to store the object, but using delete to free the memory of the old objects) run for over an hour with no problems, even if I bump the number of objects up to 150. I did have a few of the object’s variables commented out, but since don’t think I’ll want even 100 on screen at a time, I think it’s looking good for now.

@ AndyA - Again, thanks for the info. So grateful for the expertise that everyone shares on this forum. Lots to learn, but that’s part of the fun, right? :smiley:


#10

Does mBed support all the new C++ 11 features? Smart pointers, etc?


#11

@ ianlee74 - The online compiler does not conform to the C++11/14 standard.


#12

Thanks again to all who responded with helpful tips.

I’m a good deal further along now than I was yesterday, I’ve got the vector-based code working, and instead of deleting and newing up objects as they go off-screen, I just relocate them, which seems to be working great.

I’ve got a few tweaks to make in my movement calculations, as things aren’t looking quite the way I want, but the object management seems to be sorted, at least until I start adding more classes of objects. :slight_smile:

One additional question I have…probably one @ AndyA would be likeliest to know…the mbed platform compiler includes a Build tab for the program that lists the Flash and RAM used by the program. In my case, it shows 25kB Flash (78%) and 0.3kB RAM (4%) which seems quite low to me. The question: are these figures to be trusted? And I guess a secondary question…if I want to free up some of the flash, what are some strategies for doing that? I don’t really have any static consts defined, so I’m not sure where I’d look for Flash savings.


#13

@ devhammer - As you reference more libraries you introduce more code which increases the amount of FLASH memory you use. This is also one of the reasons I recommended static arrays earlier, bringing in std::vector from the STL libraries increases your code size significantly and therefore your FLASH memory utilization.

As for the build tab data, the RAM usage does not account for your dynamic memory allocations since the compiler cannot know how much RAM you will dynamically allocate at runtime. If you like I can go into the whole description of the data segments etc. but suffice to say that your global and static data is pretty much what those stats will account for, essentially everything in the .BSS and .DATA segments if you want to read more about it.

I mentioned in my first response that I would recommend you use a static array, what I should have clarified is that you should not even make that an array of pointers. Just a fixed array of objects. That way you have deterministic memory utilization that does not vary over the life of your application and then suddenly after who knows how many iterations and traversals of various code paths your code stops working because memory allocations fail.

My approach is to allocate arrays of objects, each object has a flag indicating if it is active, when I need to “allocate” a new object I just traverse the array to find an inactive instance and reuse it. You can of course do all this with std::vector, but test it you will see your FLASH memory utilization grows by a few KB as soon as you start using std::vector in your code.

So the key take away to keep your FLASH usage low, is to watch what libraries you introduce, they cost. The other thing is to use the C++ features carefully, for example introducing virtual functions into your classes will bump up your memory requirements significantly those vtables do not come cheap.

This is not necessarily the approach or suggestion I would make for desktop apps, but like NETMF vs. full .NET you are not playing by the same rules when you are targeting these tiny devices.


#14

@ devhammer - I just did a quick test to demonstrate the point

Standard OutrageousBlinky app
FLASH - 9.9kB
RAM - 0.1kB


 #include "mbed.h"

main()
{
    DigitalOut led1(LED1, false);
    
    while (true) {
        led1 = !led1;
        wait(.5);
    }    
}

Standard OutrageousBlinky app using std::vector
FLASH - 19.6kB
RAM - 0.3kB


 #include "mbed.h"
 #include <vector>

main()
{
    DigitalOut led1(LED1, false);
    vector<int> numbers;
    
    while (true) {
        numbers.push_back(1);
        led1 = !led1;
        wait(.5);
        numbers.pop_back();
    }    
}


#15

@ taylorza - Whoof! Point taken. That’s a lot of overhead, and you’ve convinced me I don’t need it.

The good news is that, thanks to you AndyA, and Jeff, I’m probably a good bit better equipped to make the switch from vector to an array. Appreciate the tips!


#16

@ devhammer - I was sadly disappointed myself when I realized all those nice STL algorithms would be of limited value on such a small device, esp. for games where they are actually quite useful.


#17

OK, so I’m trying to switch from vector to a simple object array, and not having much luck.

I’m declaring the array in the class definition like so:


    static const int MAX_OBJS = 50;
    MyGameObject objs[MAX_OBJS];

No compiler error there. But when I go to add objects to the array in a for loop, like so:


void MyGame::init() {
    disp.clear();
    MyGameObject *obj;

    for (int i = 0; i < MyGame::MAX_OBJS; i++) {
       obj = new MyGameObject;
       // compiler error on next line
       objs[i] = obj;
    }
}

I get a compiler error 349, “Error: No operator “=” matches these operands in “Main.cpp”” on the line where I am attempting to assign the new object instance to the element in the array represented by the i counter variable.

I’m sure I’m missing something simple here, but after searching around StackOverflow and reading the cplusplus.com array tutorial (http://www.cplusplus.com/doc/tutorial/arrays/), I’m not clear what I’m doing wrong.


#18

You are trying to replace an object in the array with the pointer to another newly created object.

Your array is already initialized with instances of the MyGameObject using default constructor.


#19

@ devhammer - And of course, as soon as I post the question, I figured out the answer.

Turns out that when I declare the object array, it creates the class instances, so I can get rid of all the obj = new MyGameObject stuff, and simply access the instances. Should save some initialization code, too.

And as predicted, it’s made a very big difference in the Flash use. Down to 15.4kB, which should leave plenty of room for remaining game code/elements.


#20

@ devhammer - It is hard to debug with Retro, but what I think would be useful is to do a small console test app on desktop that you can use to explore what is happening at runtime. For example.


class A
{
public:
	int i;
};

A objs[50];

int _tmain(int argc, _TCHAR* argv[])
{
	return 0;
}

If you put breakpoint on return 0 line you can do QuickWatch on the objs array (Shift+F9) and you will see that all instances are there. Just a helpful technique.