When the below code is run, if I check the memory usage of the python process, it is increasing very rapidly. This code is a simple example of what I am trying to do in my program; it isolates the problem. As you can see, I am initializing an OnscreenImage in a loop within a Task. I then do everything I can (more than should be necessary) to delete the object immediately after its creation, but somehow memory is still building up very fast, to the point where my program is running unacceptably slow after 30 seconds or less. Does anyone know what is causing this issue or how to avoid it?
def MemoryLeakTestTask(task):
for i in range(100):
image = OnscreenImage(MinimapImageFolder+'/LightGray.jpg', parent = render)
image.destroy()
image.removeNode()
del image
return Task.cont
taskMgr.add(MemoryLeakTestTask, "test")
run()
try something like this, i havnt tried this out, but this way should work…
taskMgr.add(MemoryLeakTestTask, "test")
def MemoryLeakTestTask(self,task):
i = 1
self.image = [None for i in range(100)]
for i in range(100):
self.image[i] = OnscreenImage(MinimapImageFolder+'/LightGray.jpg', parent = render)
self.remove()
return Task.cont
def remove(self):
for i in range(100):
self.image[i].remove()
run()
, you are right, thats strange! sorry i cant figure out anything…
i deleted everything also the globals, but the task with onscreen image is still sucking memory.
The bug is in texture loader.
In OnscreenImage.py, find this line :
tex = loader.loadTexture(image)
After that line, do “print id(tex)”, and you’ll see that it advances, indicating you’re getting a new copy of the same texture object in RAM. It should return the already loaded texture instead, so somehow it’s broken.
I got around it by passing Filename(image) instead of plain string.
very cool! interesting, that you dont get the same result by passing, i thought the complete onscreen is bugy. are u by passing the file in the task or seperate to?
Passing Filename() to OsI constructor or to loadTexture() call in OnscreenImage.py doesn’t cover the real hole. To cover it completely (on python side) is by passing Filename() to all TexturePool.loadTexture() calls in Loader.py.
Ah, many thanks for researching the problem! It is indeed a serious bug in Panda; I’ll be checking in a fix shortly.
The nature of the bug is a Python reference-count leak. When the interrogate-generated code attempted to coerce a string object into a Filename object automatically for the call into TexturePool.loadTexture(), it was inadvertently leaking the created Filename object. (This despite my best efforts when I wrote the code in the first place. Foo!)
Note that this diagnosis:
is actually a little misleading. This is the normal behavior (you’ll notice that id(tex) advances even in the non-leaking case). In spite of the advancing id(tex) values, each call to loader.loadTexture() is still returning the same Texture object, and you can see this with tex.this, which reports the actual C++ “this” pointer. It is, however, returning a unique Python wrapper object each time, which is why id(tex) is always different. This is the correct behavior, though; and these wrapper objects aren’t leaking, so this wasn’t the fundamental problem. The actual leak was from an internal object that never got returned to the user.
Sweet, I got it working. Thank you everyone for all of your help. Below is a sample of code that does not leak memory, but still allows initialization of OnscreenImages within the Task.
class MemoryLeakTest(object):
def __init__(self):
taskMgr.add(self.MemoryLeakTestTask, "test")
self.tex = loader.loadTexture(Filename(MinimapImageFolder+'/LightGray.jpg'))
def MemoryLeakTestTask(self,task):
for i in range(100):
image = OnscreenImage(self.tex)
image.removeNode()
return Task.cont
MemoryLeakTest()
run()
In this process I discovered another memory leak problem, with the setPos() function, which I will start another thread about soon.
Sounds like it’s probably the same bug. The fundamental memory leak was with any code that used an implicit coercion, so if you pass a 3-tuple to setPos() instead of a Point3, it has to be implicitly coerced–and therefore leaks.
Note that I’ve fixed the leak on the cvs trunk, and the fix will be included in whatever version of Panda is next released.