So, with automatic Garbage Collection, memory leaks are a thing of the past, right?
Well, not quite, of course. Automatic Garbage Collection, like most other automated programming techniques, necessarily needs to approximate (ie. guess), and while the guesses of garbage collectors are generally very good, they cannot magically predict the future.
The classic way that a program fools the garbage collector is by maintaining a global list of objects created. This is a common technique for memory management in non-garbage-collected programming environments, where such lists will be used to ensure eventual
free() of all objects in some longer-lived manager object.
Unfortunately, if such techniques carry over to garbage collected environment, the effect is to effectively disable the automatic Garbage Collection algorithm! Every object allocated will have an outstanding reference in the global list, causing garbage collection to consider all of it live data, which cannot be deallocated.
Turns out that this was a case of the above-mentioned problem. It turns out that every Papervision 3D
MaterialObject3D object registers itself in the constructor with the
MaterialManager singleton, which is nothing but a global list of allocated material objects. The result is that no material objects (which all inherit from the
MaterialObject3D class) will get deallocated ever, unless explicitly done by the application (using
MaterialObject3D::destroy() for example). So much for automatic Garbage Collection…
The problem here is a poor design in the Papervision 3D API in this respect, made even worse by the fact that the documentation does not seem to mention this aspect at all (at least I could not find it). A constructor that automagically registers the object in a global list is generally a really poor idea in garbage collected programming environments.
I am not sure why Papervision 3D does this. Maybe it could just be removed. Or alternatively, a better approach would be for the API to make it explicit that such global registration is taking place. For example by forcing the application to explicitly call some
MaterialObject3D::register(), documenting the need for unregister/destroy, or by not providing a constructor at all, and instead creating objects in some factory class, again making clear mention of the need to unregister/destroy.