Memory Leak: what's wrong with my code?

Hi,

I have a memory leak happening in the onUpdate() function of my views. Every time it is called, some memory is being allocated and not released, which after a few updates leads to an Out of Memory error.

I have reduced my code down to the following bits which seem to cause the leak, but I cannot figure out why it happens.

First, my very simple onUpdate() that I use for testing. It instantiates an container object (DummyContainer) and then adds an element to it.

function onUpdate(dc as Dc) as Void {
    System.println( "onUpdate: s " + System.getSystemStats().usedMemory );
    var obj = new DummyContainer( {} );
    obj.addElement( {} );
    System.println( "onUpdate: e " + System.getSystemStats().usedMemory );
}

Here the code for DummyContainer and two more classes used in its implementation:

class DummyContainer extends DummyBase {
    protected var _elements as Array;
    function initialize( options as Dictionary<Symbol,Object> ) {
        DummyBase.initialize( options );
        _elements = new Array[0];
    }
    function addElement( options as Dictionary<Symbol,Object> ) {
        options[:parent] = self;
        _elements.add( new DummySub( options ) );
        // new DummySub( options ); // with this line instead of the one above, there is no leak
    }
}

class DummySub extends DummyBase {
    function initialize( options as Dictionary<Symbol,Object> ) {
        DummyBase.initialize( options );
    }
}

class DummyBase {
    private var _options as Dictionary<Symbol,Object>;
    // Constructor
    function initialize( options as Dictionary<Symbol,Object> ) {
        _options = options;
       // _options = { :test => "test" }; // with this line instead of the one above, there is no leak
     }
}

As you can see the addElement function instantiates another object (DummySub) and stores it in the _elements array. Both that object and the container have the same base class (DummyBase), which stores the options dictionary in the _options member.

With the code as it is shown, the memory leak occurs. However if I either DO NOT add the DummySub to _elements OR DO NOT store the dictionary in _options, no memory leak occurs. See the alternative, commented out lines.

I do not understand why what I am doing here leads to a memory leak and what I could do differently. Any advice is highly appreciated!

So far I tested this only on the simulator - could it be a bug in the simulator and even not occur on real devices?

Here the debug output with the memory leak:

onUpdate: s 19840
onUpdate: e 20448
onUpdate: s 20448
onUpdate: e 21056
onUpdate: s 20936
onUpdate: e 21544
onUpdate: s 21544
onUpdate: e 22152
onUpdate: s 22152
onUpdate: e 22760
onUpdate: s 22760
onUpdate: e 23368

Compared to that, if I replace either of the above-mentioned lines with the commented-out version, after the first two calls, the memory stays stable:

onUpdate: s 19840
onUpdate: e 20448
onUpdate: s 19840
onUpdate: e 20448
onUpdate: s 19720
onUpdate: e 20328
onUpdate: s 19720
onUpdate: e 20328
onUpdate: s 19720
onUpdate: e 20328
onUpdate: s 19720
onUpdate: e 20328

  • In the simulator you should be able to look into the memory a bit more and maybe discover which object type is increasing in number of instances. 

  • Your code has a circular reference, caused by the following line in DummyContainer.addElement():

    options[:parent] = self;

    When addElement() is called on a DummyContainer object, it adds a new member to the _elements array consisting of a dictionary which includes a reference to the DummyContainer object itself. This circular reference will prevent the DummyContainer object from being garbage collected when it goes out of scope (with no other references pointing to it). An object will only be garbage collected when there are no references pointing to it. (Monkey C uses reference counting garbage collection.)

    DummyContainer ---> _elements[] ---> _elements[0] = { :parent }
      ^                                                    |

      |-----------------------------<----------------------v

    If you must have a reference to the parent DummyContainer in each _elements member, then you can use a WeakReference which won't prevent the object that's referenced from being garbage collected.

  • Your code has a circular reference, caused by the following line in DummyContainer.addElement():

    You caught it, thank you very much!

    I was not aware that such circular references can cause memory leaks in Monkey C. My background is mainly Java, where such structures are collected if they are cut off from everything else. I do need the reference, but changed it to a WeakReference, and am checking if it is still there before using it. Now there is no more leak! :-)

    In the simulator you should be able to look into the memory a bit more and maybe discover which object type is increasing in number of instances. 

    Is there actually a way to see local variables in the Active Memory view? I put a break point in the onUpdate() function and then looked at the Active Memory, but could not find the local variables anywhere.

  • You caught it, thank you very much!

    You're welcome!

    My background is mainly Java, where such structures are collected if they are cut off from everything else

    Yeah the tracing garbage collector in Java is a lil more sophisticated than Monkey C's reference counting

  • My 3 cents: treat onUpdate like Frame callback in game engine. Avoid all memory allocations. ALL. Redesign your code if required to use memory pool or sth. Allocating memory in onUpdate is root of all evil, especially unexpected crashes when allocation size may change.

  • Avoid all memory allocations. ALL. Redesign your code if required to use memory pool or sth.

    How would you implement such a memory pool in Monkey C? Even if I create a fixed list of objects to use, their size is subject to change, depending on what is going on in the app. In my case I process data from a web request, with a lot of strings with variable size.