Help with memory leak/eat inside cocos2d-x

Now I’m doing memory optimization and I realized that cocos eats memory. It’s not true memory leak but it something like cocos not releases consumed memory. To reproduce, please, create new project with

cocos new -l cpp test-cpp

and then inside HelloWorldScene.cpp before

auto label = Label::createWithTTF("Hello World", "fonts/Marker Felt.ttf", 24);

paste

for (int i=0; i<3000; i++) {
    Label* l = Label::createWithTTF("Hello World", "fonts/Marker Felt.ttf", 24);
}

And you’ll see that after loop your app will hold about 49Mb of RAM instead of 13.6Mb without the loop.

So, is it known behavior? or maybe I’m doing something wrong?

tested on 3.3 and 3.6 versions
bug appears on real devices so test on real devices pls

Normally you would retain those objects by adding them as children of the current layer/node using addChild(l);.

However by default they are created as autorelease objects and added to the autorelease pool and released during the next “loop”.

Where are you calling that code? Is the game running properly (i.e. you have a scene etc)?

I called it right inside newly created project with cpp lang
file called HelloWorldScene.cpp
here is file itself:

#include "HelloWorldScene.h"

USING_NS_CC;

Scene* HelloWorld::createScene()
{
    // 'scene' is an autorelease object
    auto scene = Scene::create();
    
    // 'layer' is an autorelease object
    auto layer = HelloWorld::create();

    // add layer as a child to scene
    scene->addChild(layer);

    // return the scene
    return scene;
}

// on "init" you need to initialize your instance
bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !Layer::init() )
    {
        return false;
    }
    
    Size visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();

    /////////////////////////////
    // 2. add a menu item with "X" image, which is clicked to quit the program
    //    you may modify it.

    // add a "close" icon to exit the progress. it's an autorelease object
    auto closeItem = MenuItemImage::create(
                                           "CloseNormal.png",
                                           "CloseSelected.png",
                                           CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));
    
	closeItem->setPosition(Vec2(origin.x + visibleSize.width - closeItem->getContentSize().width/2 ,
                                origin.y + closeItem->getContentSize().height/2));

    // create menu, it's an autorelease object
    auto menu = Menu::create(closeItem, NULL);
    menu->setPosition(Vec2::ZERO);
    this->addChild(menu, 1);

    /////////////////////////////
    // 3. add your codes below...

    // add a label shows "Hello World"
    // create and initialize a label
    
    for (int i=0; i<3000; i++) {
        auto l = Label::createWithTTF("Hello World", "fonts/Marker Felt.ttf", 24);
        
        //this retain and release changes nothing, tried with and without it
        l->retain ();
        l->release ();
    }
    
    auto label = Label::createWithTTF("Hello World", "fonts/Marker Felt.ttf", 24);
    
    // position the label on the center of the screen
    label->setPosition(Vec2(origin.x + visibleSize.width/2,
                            origin.y + visibleSize.height - label->getContentSize().height));

    // add the label as a child to this layer
    this->addChild(label, 1);

    // add "HelloWorld" splash screen"
    auto sprite = Sprite::create("HelloWorld.png");

    // position the sprite on the center of the screen
    sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));

    // add the sprite as a child to this layer
    this->addChild(sprite, 0);
    
    return true;
}


void HelloWorld::menuCloseCallback(Ref* pSender)
{
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WP8) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT)
	MessageBox("You pressed the close button. Windows Store Apps do not implement a close button.","Alert");
    return;
#endif

    Director::getInstance()->end();

#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
    exit(0);
#endif
}

I don’t think Label::createWithTTF is optimized nor designed for creating 3000 labels. BMFont would probably handle this loop fine. If you try the same thing with Sprite I would be you don’t see much of a RAM spike.

for (int i=0; i<10000; i++) {
    auto s = Sprite::create ();
}

also consumes about 6-7 Mb of RAM and never return it

What do you mean by “return” the memory? The interaction between the application and the operating system is quite complex-- you could read: http://stackoverflow.com/questions/12050777/how-to-return-memory-from-process-to-the-os for some guidance on that.

Is the auto-release pool never calling delete on the Sprite? (set a breakpoint on the destructor should be a sufficient way to find out.) Or are there other pointers that are never deleted? It’s possible that there is a memory leak, but it’s also possible that Cocos2d-x has freed the memory but it’s not available for release to the program. A better test would be to create 10,000 sprites wait 10 second and then create 10,000 more a few times. That should not successively eat up more memory as you should use the same chunk of memory.

Sorry if this is a wrong or stupid answer–I’m a bit of a novice but this is the first and obvious answer that comes to mind.

What platform are you testing on real device? iOS device? Android? I presume it’s mobile since testing on Mac/Win is effectively running on device. Also are you testing release build to make sure it’s not debugging overhead. How are you profiling.


I think you’ll need to profile it in a manner that is easier to determine if it’s a leak to prove it’s a cocos2d-x bug, and if it is file an issue. They’re usually quick to address leak issues.

Schedule an update to fire at every second interval. Every update do what @gkapur recommends and create 10000 (or 1000 since you’ll create many many).

Does the RAM continually increase forever? Does it eventually crash (try 10000 or 100000 per update)? Are you profiling the leak correctly to make sure you’re looking at actual active RAM usage? Do you get low memory warning calls on device?

I’ll setup a test and see how it profiles as well.

I think @gkapur is right about complex memory interaction. I did 2 tests:

int iteri = 0;
schedule([iteri] (float t) mutable {
    CCLOG ("iter: %d", ++iteri);
    for (int i=0; i<50000; i++) {
        auto s = Sprite::create ();
    }
}, 2, 100, 0, "asd");

saturation appears in about 11 timeouts

secod test with Label:

int iteri = 0;
schedule([iteri] (float t) mutable {
    CCLOG ("iter: %d", ++iteri);
    for (int i=0; i<2000; i++) {
        auto l = Label::createWithTTF("Hello World", "fonts/Marker Felt.ttf", 24);
    }
}, 1, 100, 0, "asd");

saturation again in about at 11 timeout

I tested it on iphone4s. Memory warning appears rarely, I think it is because a lot of memory consumed for a short time. I tested on debug mode, I think, just plugged the device and run on it

Nice. Glad you tested, hopefully it’s showing you a few things. Note this memory details pane is for overview and not a great method for finding specific memory issues. The Instruments Leaks and Allocations will show you actual information about the details of an apps memory usage.

I’ll take your snippets and run them through instruments sometime this week to glean a little more information about what is going on.