I’m using panda3d for a reinforcement learning project. Part of RL is to generate the same simulated environment thousands, millions of times, and test how a smart agent interacts with it. I’m finding that every time I create/destroy the environment I burn a few hundred kb of memory, and eventually that kills the machines I’m running my experiments on.
The full project isn’t ready for public release, but I’ve put together a torn down version here which can be run directly from the commandline with minimal setup. There are instructions at the top of the file. It creates and destroys a very simple procedurally-generated environment ten times, and uses panda3d’s MemoryUsage
module to see what’s left lying behind. When I run the script (on panda 1.10.1 and macOS 10.14.3), this is the output:
Leaked 0.13MB of memory in 10 loops. Pointer counts follow:
LightAttrib PerspectiveLens PointLight RenderState Texture TextureAttrib
0 1 6 1 1 1 1
1 2 12 2 2 2 2
2 3 18 3 3 3 3
3 4 24 4 4 4 4
4 5 30 5 5 5 5
5 6 36 6 6 6 6
6 7 42 7 7 7 7
7 8 48 8 8 8 8
8 9 54 9 9 9 9
9 10 60 10 10 10 10
Each row reports the state of the memory after a create/destroy iteration, and as you can see it burns about 10KB/loop and leaves various light and texture objects lying around. There are two lines in the script marked #TODO
which are the culprits, and commenting them out zeros the memory leak.
Most of the script is memory tracking. The scene itself is the nigh-on-trivial
root.setShaderAuto()
# Add a textured surface
surf = root.attach_new_node(GeomNode('wall'))
tex = Texture()
tex.setup_2d_texture(256, 1, Texture.T_unsigned_byte, Texture.F_luminance)
surf.set_texture(tex) # TODO: This is the line that generates half the memory leaks.
# Add point lights
point = root.attach_new_node(PointLight('point_light'))
root.set_light(point) # TODO: This is the line that generates the other half of the memory leaks.
root.flattenStrong()
I’m already removing all nodepaths, clearing the render cache and clearing the texture cache with
# Remove all the nodes I created
for p in descendents(root):
p.remove_node()
# Clear various caches
TransformState.garbage_collect()
RenderState.garbage_collect()
What am I missing? Where are these remnants recorded? How do I kill them dead?
Edit: Now with newly-simplified script