GUI memory leak

You are an experienced programmer and have a problem with the engine, shaders, or advanced effects? Here you'll get answers.
No questions about C++ programming or topics which are answered in the tutorials!
Post Reply
Seven
Posts: 1030
Joined: Mon Nov 14, 2005 2:03 pm

GUI memory leak

Post by Seven »

I've been racking my brain on this one. Hopefully someone can straighten me out on it.
My GUI system is based on the Irrlicht GUI system and I have a memory leak that I cannot find.
Here is a simple example that shows the issue. Any help would be greatly appreciated.

Debug mode gives me this error when exiting the program, pointing to the statictext object that was created

Code: Select all

Detected memory leaks!
Dumping objects ->
{1995} normal block at 0x000001EBC27636C0, 16 bytes long.
 Data: <T e s t i n g   > 54 00 65 00 73 00 74 00 69 00 6E 00 67 00 00 00 
{1994} normal block at 0x000001EBC79E3660, 1 bytes long.
 Data: < > 00 
{1993} normal block at 0x000001EBC79E3320, 2 bytes long.
 Data: <  > 00 00 
D:\_Programming\Irrlicht Game Engine\_Externals\Irrlicht\source\Irrlicht\CGUIEnvironment.cpp(1307) : {1991} client block at 0x000001EBC277C900, subtype 0, 416 bytes long.
 Data: <` +       +     > 60 F3 2B 14 FB 7F 00 00 98 F5 2B 14 FB 7F 00 00 
Object dump complete.
The program '[19064] 000_SandBox.exe' has exited with code 0 (0x0).

Code: Select all

// 000_SandBox.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include <iostream>
#include "Irrlicht.h"

#ifdef _DEBUG
#pragma comment(lib, "Irrlicht64_debug.lib")	// link to the correct library based on build type
#else
#pragma comment(lib, "Irrlicht64.lib")			// link to the correct library based on build type
#endif

using namespace irr;
using namespace core;
using namespace scene;
using namespace io;
using namespace scene;
using namespace gui;
using namespace video;

IrrlichtDevice* gDevice = nullptr;
IVideoDriver*   gDriver = nullptr;
ISceneManager*  gSmgr   = nullptr;
IGUIEnvironment* gGUI   = nullptr;

#define MY_ELEMENT_TYPE 200

// derive a new class from the base guielement class
class myGuiElement : public IGUIElement
{
public:
    myGuiElement(IGUIEnvironment* environment, IGUIElement* parent, s32 id, const core::rect<s32>& r)
        : IGUIElement((EGUI_ELEMENT_TYPE)MY_ELEMENT_TYPE, environment, parent == nullptr ? environment->getRootGUIElement() : parent, id, r)
    {
        // drop this to keep the refrencecounter correct
        drop();
    }
};

// derive a new class from the myGuiElement class 
class myGuiElement_StaticText : public myGuiElement
{
public:
    myGuiElement_StaticText(IGUIEnvironment* environment, IGUIElement* parent, s32 id, const core::rect<s32>& r)
        : myGuiElement(environment, parent, id, r)
    {
        // add a child statictext to this instance
        IGUIStaticText* st = environment->addStaticText(L"Testing", r, true, false, this, id, true);
    }
};


void createScene()
{
    // create an instance with the gGUI rootnode as the parent
    myGuiElement* e = new myGuiElement(gGUI, nullptr, 1, recti(100, 100, 300, 400));

    // create an instance with the  above 'e' as the parent
    myGuiElement_StaticText* t = new myGuiElement_StaticText(gGUI, e, 1, recti(0, 0, 100, 30));
}

int main()
{
    gDevice = createDevice(video::EDT_OPENGL, dimension2d<u32>(640, 480), 16, false, false, false, 0);
    if (!gDevice)  return 1;
    gDriver = gDevice->getVideoDriver();
    gSmgr = gDevice->getSceneManager();
    gGUI = gDevice->getGUIEnvironment();

    createScene();

    while (gDevice->run())
    {
        gDriver->beginScene(true, true, SColor(255, 100, 101, 140));
        gSmgr->drawAll();
        gGUI->drawAll();
        gDriver->endScene();
    }
    gDevice->drop();
    return 0;
}
CuteAlien
Admin
Posts: 9644
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: GUI memory leak

Post by CuteAlien »

First - don't use drop() in a constructor. Thought I suppose this is for testing here or something. But probably this is not about myGuiElement anyway - you should get the leak even if you kick that out of the test.

You create myGuiElement_StaticText and never drop() it. Bascially if you use new() on a reference counted object or use a function in Irrlicht which starts with "create" you have to call drop() or it will leak. So your 't' will leak. The 'e' won't because of your strange drop() in the constructor - which is bad as it breaks the usual behavior (aka you should also drop it after creating it when you no longer need the variable).

Maybe what is confusing you is that all gui-elements are always added to their parent in the constructor. And the parents (or the gui environment if you have no parent) will then grab() them (because it has a pointer to it's child it grabs()/drops() internally). I wish constructors wouldn't do that and adding would be explicited sometimes, but I guess I just got used to it by now.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Seven
Posts: 1030
Joined: Mon Nov 14, 2005 2:03 pm

Re: GUI memory leak

Post by Seven »

thanks for the reply. I know that you are busy so it is appreciated. this particular issue has been around in my code forever and I just gave up on it so long ago. however, I am repurposing my code base (actual game in progress at this point) and need to fix this before i can publish.

I reduced the code to a bare minimum and added some printf's into it to show myself what is happening. As best as I can tell, when I create a derived class like myGuiElement (derived from IGUIElement) and try to add a child to it, no matter what I do i cant get rid of the memory leak unless I track the child and remove it manually.

here is the newest test code showing what I am seeing.
I made the variables global and / or public so that I can track them throughout the program life.

Code: Select all

// 000_SandBox.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include <iostream>
#include "Irrlicht.h"

#ifdef _DEBUG
#pragma comment(lib, "Irrlicht64_debug.lib")	// link to the correct library based on build type
#else
#pragma comment(lib, "Irrlicht64.lib")			// link to the correct library based on build type
#endif

using namespace irr;
using namespace core;
using namespace scene;
using namespace io;
using namespace scene;
using namespace gui;
using namespace video;

IrrlichtDevice* gDevice = nullptr;
IVideoDriver*   gDriver = nullptr;
ISceneManager*  gSmgr   = nullptr;
IGUIEnvironment* gGUI   = nullptr;

#define MY_ELEMENT_TYPE 200

// derive a new class from the base guielement class
class myGuiElement : public IGUIElement
{
public:
    IGUIStaticText* st = nullptr;
public:
    myGuiElement(IGUIEnvironment* environment, IGUIElement* parent, s32 id, const core::rect<s32>& r)
        : IGUIElement((EGUI_ELEMENT_TYPE)MY_ELEMENT_TYPE, environment, parent == nullptr ? environment->getRootGUIElement() : parent, id, r)
    {
        printf("\n%d myGUIElement constructor reference count\n", getReferenceCount());

        // add a child statictext to this instance
        st = environment->addStaticText(L"Testing2", r, true, false, this, id, true);
        printf("%d statictext reference count\n", st->getReferenceCount());

        printf("%d myGUIElement constructor reference count after statictext creation\n", getReferenceCount());
    }
};

myGuiElement* e = nullptr;

void createScene()
{
    // create an instance with the gGUI rootnode as the parent
    e = new myGuiElement(gGUI, gGUI->getRootGUIElement(), 1, recti(100, 100, 300, 400));
    printf("%d (e)   reference count (before drop())\n", e->getReferenceCount());
    printf("%d (st)  reference count (before drop())\n", e->st->getReferenceCount());
    e->drop();
    printf("%d (e)   reference count (after drop())\n", e->getReferenceCount());
    printf("%d (st)  reference count (after drop())\n\n", e->st->getReferenceCount());
}

int main()
{
    gDevice = createDevice(video::EDT_OPENGL, dimension2d<u32>(640, 480), 16, false, false, false, 0);
    if (!gDevice)  return 1;
    gDriver = gDevice->getVideoDriver();
    gSmgr = gDevice->getSceneManager();
    gGUI = gDevice->getGUIEnvironment();

    createScene();

    while (gDevice->run())
    {
        gDriver->beginScene(true, true, SColor(255, 100, 101, 140));
        gSmgr->drawAll();
        gGUI->drawAll();
        gDriver->endScene();
    }

//define FIX_IT
#ifdef FIX_IT
    printf("myGuiElement Instance reference count = %d  statictext reference count = %d\n", e->getReferenceCount(), e->st->getReferenceCount());
    e->st->remove();
#endif

    gDevice->drop();
    return 0;
}

and the output of this code looks like this.

Code: Select all

2 myGUIElement constructor reference count
1 statictext reference count
2 myGUIElement constructor reference count after statictext creation
2 (e)   reference count (before drop())
1 (st)  reference count (before drop())
1 (e)   reference count (after drop())
1 (st)  reference count (after drop())
what I cant figure out is why (since both the (e) variable and the (e->st) variable have a reference count of 1), the (e) variable does not destroy the (e->st) variable when the device is dropped. I also have the memory leak if I call e->remove(), which again leads me to think that (e) is not destroying it's own child for some reason. however, if i track the (e->st) variable and call remove manually there is no memory leak. (I.E. FIX_IT)

as I say, I struggled with this literally years ago and just gave up on it with no fix but I really need to understand it now to move forward.

thanks for any help you gave give!
CuteAlien
Admin
Posts: 9644
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: GUI memory leak

Post by CuteAlien »

Hm, right, e->remove() should already do the trick. And otherwise on device drop. I'll check in the evening, doesn't look wrong on a quick read.
Or do you maybe quit by closing the console window which you see in Windows? That stops the application without cleaning it up.

edit: I just quickly run it. Not getting memory leaks when copy-pasting your code above (FIX_IT not enabled). Only when I quit by pressing console window - but then I get far more than just the static-text, so it's likely not what you do.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Seven
Posts: 1030
Joined: Mon Nov 14, 2005 2:03 pm

Re: GUI memory leak

Post by Seven »

hmmm. I close the app with the close button on the irrlicht window but not the console, at least normally :)
maybe I need an obj file cleanup and complete rebuild. I will spend some time on it tomorrow also. thanks for taking a look!
Post Reply