A while back I promised I would write a new page for the Panda3D manual to cover garbage collection, because it is such an important topic and it has so little coverage so far. I’m way, way past the deadline I set for myself on this, but I haven’t forgotten about it. I’ve been collecting and compiling what knowledge I could about garbage collection since then. I’ve put that knowledge to work in the Panda3D Beginner’s Guide that Packt Publishing has commissioned me to write, and I thought I would post a segment from that book here. This is not necessarily a complete guide to garbage collection, but it does cover all of the steps necessary for the game created over the course of the book.
According to my contract I have the right to reuse segments of the book for other writings, so long as they do not constitute more than 10% of the book and I credit the book as the source. Therefore, I have the right to reuse this material for the Panda3D manual, but the manual page must note that this writing comes from Panda3D: Beginner’s Guide - How to Build a Complete Game from Packt Publishing.
I leave it up to those who manage the manual as to whether or not that sort of psuedo advertisement is acceptable to put in the manual.
If anything in this writing is in error, please let me know.
Python will automatically garbage collect a custom class instance when all the references to that instance are removed. In theory, this makes garbage collection as simple as cleaning up those references, but because there are so many different places and reasons for these references garbage collection can quickly grow complicated. Following these steps will help to ensure that a custom class instance is properly garbage collected.
-
Call removeNode on all NodePaths in the scene graph – The first step is to clear out the NodePaths that the custom class has added to the scene graph. If this step isn’t accomplished, it won’t necessarily prevent the custom class instance from being garbage collected, but it could. Even if the custom class instance is still garbage collected the scene graph itself will retain references to the NodePaths that haven’t been cleared out and they will remain in the scene graph. There is one exception to this rule: when a parent NodePath has removeNode called on it that ultimately result in the removal of its child NodePaths, so long as nothing else retains a reference to them. However, relying on this behaviour is an easy way to make mistakes so it’s better to manually remove all of the NodePaths a custom class adds to the scene graph.
-
Call delete on all Actors – Just calling removeNode on an actor isn’t enough. Calling delete will remove ties to animations, exposed joints, and so on to ensure that all the extra components of the Actor are removed from memory as well.
-
Set all Intervals, Sequences, and Parallels equal to None – It’s very common for Intervals, Sequences, and Parallels to retain references to something in the class and prevent the class instance from being cleaned up. To be safe, it’s best to remove the references to these Intervals so that they get cleaned up themselves and any references they have to the class are removed.
-
Detach all 3D sounds connected to class NodePaths – 3D sounds won’t actually retain references to the custom class, but if the NodePaths they are attached to are removed with removeNode and the sounds aren’t detached, they’ll generate an error and crash the program when they try to access the removed NodePaths. Play it safe and detach the sounds.
-
End all tasks running in the class – The task manager will retain a reference to the class instance so long as the class instance has a task running, so set up all of the tasks in the custom class to end themselves with return task.done. This is the most reliable way to stop them and clear the reference to the custom class in the task manager.
-
If the custom class inherits from DirectObject, call self.ignoreAll() – Panda3D’s message system will also retain a reference to the custom class if it is set up to receive messages. To be on the safe side, every class that inherits from DirectObject and will be deleted during run time should call self.ignoreAll() to tell the message system that the class is no longer listening to messages. That will remove the reference.
-
Remove all direct references to the custom class instance – Naturally, the custom class instance won’t get cleaned up if something is referencing it directly, either through a circular self reference, or because it was created as a “child” of another class and that other class has a reference to it stored as a variable. All of these references need to be removed. This also includes references to the custom class instance placed in PythonTags.
The del method is a good way to test if a custom class is being garbage collected. The del method is similar to the init method in that we don’t call it ourselves; it gets called when something happens. init is called when a new instance of the class is created; del is called when an instance of the class is garbage collected. It’s a pretty common thought to want to put some important clean up steps in the del method itself, but this isn’t wise. In fact, it’s best not to have a del method in any of our classes in the final product because the del method can actually hinder proper garbage collection. A better usage is to put a simple print statement in the del method that will serve as a notifier that Python has garbage collected the custom class instance. For example:
def __del__(self):
print("Instance of Custom Class Alpha Removed")
Once we’ve confirmed that our custom class is being garbage collected properly, we can remove the del method.