Probably memory leak or "I don't understand how to release memory"

Hi! I use cocos2d-x C++ (version 2.2.0 release). I have problem with memory.

  1. I understand how to use autorelease(), release() and retain() methods.
  2. I have a texture (texture.png) and create a CCSprite which as I know is autorelease and attach it to the scene (also autorelease).
  3. When I switch my scene program is “free” my texture.png after I call removeUnusedTextures() from TextureCache.
  4. I don’t have any memory leaks at profiler.
  5. But memory of my application is growing after each replace scene.

What I am doing wrong?
Thanks for you help!

There are two ways to fix issue: refactoring which sanitizes code and direct tracing.

The first way is to make all nodes to use clean CCNode lifecycle:
# C++ constructor sets all raw pointers and numbers to 0 (but it’s better to have smart pointers which automatically init nested pointer with 0)
# init[...]([...]) creates children nodes and attaches them to this node
# onEnter() turns on actions, scheduled methods, scheduled update, touch/keypad/IME event subscriptions
# onExit() stops all actions, scheduled methods, scheduled update, touch/keypad/IME event subscriptions
# cleanup() finally removes node from own arrays/dictionaries for remove all links to it, so after calling cleanup() parent will remove object from own dictionary m_children which should be the last link to node, node will be deleted.

Second way includes a little or a big tweak in CCNode sources: create global static variable in CCNode.cpp which will keep an std::vector or std::set of living nodes, modify this array from CCNode constructor and destructor. You’ll also need IDE which can display content of std::vector or std::set in debugger (QtCreator, probably Visual Studio and XCode can do it).

We’ve used more complex approach: added method which collects functions stack trace at given point and stored stacktrace each time when retain/release was called for CCNode, so we’ve seen who keeps link to node.

Sergey Shambir wrote:

We’ve used more complex approach: added method which collects functions stack trace at given point and stored stacktrace each time when retain/release was called for CCNode, so we’ve seen who keeps link to node.

hi, how did you collect function stack trace? I’m very interested at this~

Sergey Shambir wrote:

There are two ways to fix issue: refactoring which sanitizes code and direct tracing.
>
The first way is to make all nodes to use clean CCNode lifecycle:
# C++ constructor sets all raw pointers and numbers to 0 (but it’s better to have smart pointers which automatically init nested pointer with 0)
# init[...]([...]) creates children nodes and attaches them to this node
# onEnter() turns on actions, scheduled methods, scheduled update, touch/keypad/IME event subscriptions
# onExit() stops all actions, scheduled methods, scheduled update, touch/keypad/IME event subscriptions
# cleanup() finally removes node from own arrays/dictionaries for remove all links to it, so after calling cleanup() parent will remove object from own dictionary m_children which should be the last link to node, node will be deleted.
>
Second way includes a little or a big tweak in CCNode sources: create global static variable in CCNode.cpp which will keep an std::vector or std::set of living nodes, modify this array from CCNode constructor and destructor. You’ll also need IDE which can display content of std::vector or std::set in debugger (QtCreator, probably Visual Studio and XCode can do it).
>
We’ve used more complex approach: added method which collects functions stack trace at given point and stored stacktrace each time when retain/release was called for CCNode, so we’ve seen who keeps link to node.

Thanks for your replay!
I doing all this things but still have a problem with memory :frowning:

Can I provide you an example of what I do and maybe you will have some time to explain me where is my mistake? Please.

So.

CCSomeClass.h:

class CCSomeClass : public CCSprite
{
 protected:
  int m_SomeField;

  CCSprite* m_SomeSpriteField;

  CCSomeClass(/** Maybe some params */);

 public:
  static CCSomeClass* create(/** Maybe some params */);

  ~CCSomeClass();

  void onEnter();
  void onExit();
}

CCSomeClass.cpp:

CCSomeClass::~CCSomeClass()
{
 this->removeAllChildrenAndCleanup(true); // Or this->cleanup(); Does't matter.
}

CCSomeClass::CCSomeClass(/** Maybe some params */) :
CCSprite(/** Maybe some params */),
m_SomeField(0),
m_SomeSpriteField(0)
{
 // Do some stuffs.
}

CCSomeClass* CCSomeClass::create(/** Maybe some params */)
{
 CCSomeClass* cl = new CCSomeClass(/** Maybe some params */);
 cl->autorelease();
 cl->retain(); // Use retain here for access this object if I need it and really don't forget to call release() after this object don't need me more.

 return cl;
}

void CCSomeClass::onEnter()
{
 CCSprite::onEnter();

 this->scheduleUpdate(); // Need it for use update(float deltaTime); method;
}

void CCSomeClass::onExit()
{
 CCSprite::onExit();

 this->unscheduleUpdate();
}

So after this object no need me no more and I release it by calling release(); or CC_SAFE_RELEASE(); the texture it “hold” is unloading but memory are not :frowning:

Where is my mistake? Please help me. Thanks!

i debug my exe at windows also show the problem,and i put a break point at the CCAutoreleasePool.When enter the init method of my helloworld,there create two CCAutoreleasePool instance,and they are put at the CCPoolManager.When the below code happens
void CCDisplayLinkDirector::mainLoop(void) { if (m_bPurgeDirecotorInNextLoop) { m_bPurgeDirecotorInNextLoop = false; purgeDirector(); } else if (! m_bInvalid) { drawScene(); // release the objects CCPoolManager::sharedPoolManager()->pop(); } }

there is only one CCAutoreleasePool there without create new CCAutoreleasePool instance.so when you replace your scene,every spites you create are put to the first CCAutoreleasePool instance,which is
being release at the end.
the solution maybe you can add CCPoolManager::sharedPoolManager()->push(); in front of drawScene();

Thanks for your replay but I know that all my objects is 100% has been released! This is the problem.

Sure, maybe it’s my fault but now I remove autorelease() everywhere and rewrote my code the simple new and delete keywords and it’s work fine!

You need to release all sprite frames that use the texture you want to remove. If you take a look at CCSpriteFrame codes you may find the following:

if (pobTexture)
{
pobTexture~~>retain;
}

And I would strongly recommend to Ctrl+Z back from manually calling new and delete, most likely that will lead you to severe pains in~~the ass- future.

Snippet [1] implements function std::string ccGetStackTrace(const std::string &title) that returns string with 10 stack traces. Works on Linux, MacOSX, iOS, android.

You can define CCObject::retain() and CCObject::release() as virtual, override them in CCNode, create CCNode member of type std::vector<std::string> to keep stacktraces from all retain/release requests and so debug who keeps link on your node. Don’t forget to also keep global set of living nodes — otherwise you’ll not able to access stacktraces from debugger.

From my experience, such tracing makes app 4 times slower.

[1] http://paste.ubuntu.com/6290087/

Sergey Shambir wrote:

Snippet [1] implements function std::string ccGetStackTrace(const std::string &title) that returns string with 10 stack traces. Works on Linux, MacOSX, iOS, android.
>
Didn’t know the existence of such API before, will try this out, in debug mode, so hopefully no need to worry about performance.

Thanks a lot!

I have the same question, and desperately miss new/delete now.