How to completely remove a node and its children

Return to Scripting Issues

How to completely remove a node and its children

Postby ThePurpleAlien » Tue Jul 03, 2012 2:44 pm

I am developing an app that does very frequent redraws of the whole scene, i.e. dumping the entire scene and drawing a new one.

When I watch my app's memory use in the task manager, I can see that it steps up every time the scene is redrawn, but no memory is released when I tear down the old scene, so it just keeps going up which eventually leads to a crash. I assume I'm not properly cleaning up the old scene before I start drawing the new one.

I'm using removeNode to delete the old scene. At first I thought calling removeNode cleaned up everything at and below the calling nodepath. But a bit of experimenting showed that if you keep a reference to a child nodepath, it won't be cleaned up when you call removeNode on the parent. Does removeNode clean up children that don't have any references? What is the most efficient way to clean up everything under a specified nodepath regardless of whether there are references to some of its children?

Thanks.
ThePurpleAlien
 
Posts: 19
Joined: Thu Aug 19, 2010 10:15 pm
Location: Canada

Postby Thaumaturge » Tue Jul 03, 2012 3:01 pm

I don't know about the behaviour of removeNode, but I suspect that you're running into Python reference counting issues; specifically, that as long as a reference to an object exists (even if it's locked in a cyclic reference to which you've lost the handles) then the related memory likely won't be released, at least until the program is closed.
MWAHAHAHAHA!!!

*ahem*

Sorry.
User avatar
Thaumaturge
 
Posts: 685
Joined: Sat Jun 07, 2008 6:34 pm
Location: Cape Town, South Africa

Postby rdb » Wed Jul 04, 2012 12:29 pm

removeNode detaches the node from the scene graph and drops the reference, so that (assuming you're not keeping any other references) it may be garbage collected.

Deleting a scene works like deleting any object in Python - you don't, you just drop all the references to it, so that it is garbage collected.

Parents hold references to child nodes, though, so that's why there's removeNode() which also detaches the given node from the scene graph.

There is also removeAllChildren, but it doesn't do any magical deleting either - it simply invokes removeNode on all children of the node, but this won't have a particular benefit over removeNode if you cleared up your references correctly. In that sense, removeAllChildren doesn't solve your problem, since the child nodes won't be removed either until their references are cleared as well.

One thing to mention is that if you have used setPythonTag referring to another node on any of the nodes, you have created a circular reference which must be explicitly broken first before you drop your last reference.
rdb
 
Posts: 8565
Joined: Mon Dec 04, 2006 5:58 am
Location: Netherlands

Postby ThePurpleAlien » Wed Jul 04, 2012 12:59 pm

removeNode is different from just dropping a reference and waiting for the garbage collector though. For example:

a = render.attachNewNode('Something')
b = a
a.removeNode()
b.setName('SomethingElse')

The last line in this code raises an is_empty assertion error, so a.removeNode() does delete the node even though a and b still reference the node path.

So presumably, traversing the whole scene graph and calling removeNode on each node path would tear down the whole scene and release memory regardless of there being any references to some of the node paths. Those references would just become empty like the above code.

I'm just wondering if there's a faster way to do this rather than manually traversing the whole scene graph.
ThePurpleAlien
 
Posts: 19
Joined: Thu Aug 19, 2010 10:15 pm
Location: Canada

Postby ThePurpleAlien » Wed Jul 04, 2012 2:22 pm

I realize I was wrong in my last reply. A bit more experimenting has shown me what rdb was saying.

x.removeNode() removes that nodepath from its parent and drops the reference from x to the nodepath so the branch starting at x can be garbage collected.

But a single reference to any child of x will result in the whole branch not being garbage collected at all because node paths keep references to both parent and children.

I think circular references using setPythonTag might be the source of my memory leak. Thanks.
ThePurpleAlien
 
Posts: 19
Joined: Thu Aug 19, 2010 10:15 pm
Location: Canada


Return to Scripting Issues

Who is online

Users browsing this forum: No registered users and 0 guests