Memory leak

Hi everyone,

My game is… finished BUT my tester found that after a couple of minute (like 10 minutes) the game starts to slow down and then crash. I discover by looking at the memory usage in windows that the game goes linearly from 200 meg of usage to 1 GB.

So I put in comments a lot of thing and discover that it’s entirely related to my weapon system. Each time the player or the enemies shoot a lazer (or any other weapon), the memory increase of approximatly 1 meg. If I do nothing, memory is stable.

I check if the class were deleted (i put a “print” inside of del) and everything seems fine. I also remove each node… So I don’t understand where all this memory is lost. Or maybe the garbage collector do not work well??? I’m new to Python so maybe I’m missing something. Please help because this project is due really soon and I was to write a report on Panda3D telling that everything was perfect but with this memory problem, I don’t know what I will do!

So here is the code of the weapon itself:

from pandac.PandaModules import Vec3, CollisionSphere, CollisionNode
from direct.interval.IntervalGlobal import *
import sys 
import types 

class WeaponDarkLazer:
    def __init__(self, world, basePos, baseHpr):
        self.world = world
        self.myID = world.getNextID()
        self.pos = basePos
        self.baseHpr = baseHpr
        self.hitDammage = 1
        self.speed = 60
        self.autodestruction = 3
        self.totalElapsed = 0
        self.isDead = 0
        self.loadModels()
        self.loadSound()
        self.playSound()
        self.print_top_100()
        
    def doKill(self, id):
        if (id == self.myID):
            self.isDead = 1 
            return 1
        else:
            return 0
            
    def getPos(self):
        self.returnValue2 = Vec3(self.leftBeam.getX(), self.leftBeam.getY() + (self.speed*self.totalElapsed), self.leftBeam.getZ())
        return self.returnValue2
    
    def getHpr(self):
        return self.root_lazer.getHpr()
    
    def remove(self):
        self.root_lazer.removeNode()
        
    def loadSound(self):
        self.lazersound = loader.loadSfx("sound/lazer.wav")
        self.lazersound.setVolume(0.6)
        self.lazersound.setLoop(False)
        
    def playSound(self):
        self.lazersound.play()
        
    def loadModels(self):
        self.root_lazer = self.world.attachNode()
        self.root_lazer.setPos(0, 0, 0)
        self.root_lazer.setHpr(self.baseHpr)
        
        #Load the beams
        self.leftBeam = loader.loadModel("models/darklazer/cylinder.egg")
        self.leftBeam.reparentTo(self.root_lazer)
        self.leftBeam.setColor(0, 1, 0, 1)
        self.leftBeam.setHpr(0, 90, 0)
        self.leftBeam.setScale(0.04, 0.04, 0.5)
        self.leftBeam.setPos(self.pos)
        self.leftBeam.setX(self.leftBeam.getX() - 0.5)
        self.leftBeam.show()
        
        self.rightBeam = loader.loadModel("models/darklazer/cylinder.egg")
        self.rightBeam.reparentTo(self.root_lazer)
        self.rightBeam.setColor(0, 1, 0, 1)
        self.rightBeam.setHpr(0, 90, 0)
        self.rightBeam.setScale(0.04, 0.04, 0.5)
        self.rightBeam.setPos(self.pos)
        self.rightBeam.setX(self.rightBeam.getX() + 0.5)
        self.rightBeam.show()
        
        #Load la bulle de collision
        self.cslazer1 = CollisionSphere(self.pos.getX() - 0.5, self.pos.getY() + 0.5, self.pos.getZ(), 0.2) 
        self.cslazer2 = CollisionSphere(self.pos.getX() + 0.5, self.pos.getY() + 0.5, self.pos.getZ(), 0.2)
        self.cnodePathlazer = self.root_lazer.attachNewNode(CollisionNode('cnode')) 
        self.cnodePathlazer.node().addSolid(self.cslazer1)
        self.cnodePathlazer.node().addSolid(self.cslazer2) 
        self.cnodePathlazer.setTag('collisionType', 'weapon')
        self.cnodePathlazer.setTag('damage', str(self.hitDammage))
        self.cnodePathlazer.setTag('ID', str(self.myID))
        #self.cnodePathlazer.show()
        self.world.addToCollisionSystem(self.cnodePathlazer)
        
    def move(self, elapsed):
        self.totalElapsed += elapsed
        
        if (self.totalElapsed < self.autodestruction):
            self.root_lazer.setFluidPos(self.root_lazer,Vec3(0,self.speed*elapsed,0))
        else:
            self.isDead = 1
            
    def isWeaponDead(self):
        return self.isDead

And here is how I move the weapon at each refresh:

if (len(self.enemyProjectile) > 0):
            for weapon in self.enemyProjectile:
                if (weapon.getType() == "phantom"):
                    weapon.move(elapsed, self.fighter.getFighter())
                else:
                    weapon.move(elapsed)
                for deadID in self.enemyDeadWeaponList:
                    if (weapon.doKill(deadID) == 1):
                        self.world.fighterReceiveExplosion()
                        
                for deadID in self.enemyDeadWeaponSilentList:
                    weapon.doKill(deadID)
                        
                if (weapon.isWeaponDead() == 1):
                    self.enemyProjectile.remove(weapon)
                    weapon.remove()
            
        del self.enemyDeadWeaponList[:] 
        del self.enemyDeadWeaponSilentList[:]

Please help me!!!

Jaff

P.S: If you have technique to hunt memory leak, please explain them well! Thanks!!!

It could be the collision system get overwhelmed by the lazers, but not released during remove().

Try this at each refresh :

print yourCollisionTraverser.getNumColliders()

It must piles up as you shoot.

your lazer’s removal :

    def remove(self):
        self.root_lazer.removeNode()
        yourCollisionTraverser.removeCollider(self.cnodePathlazer)

Perhaps removing the audio helps too.

I second ynjh_jo. Try commenting out the audio code and see what happens.

You just save my project!!!

First for the sound, it saves a lot of memory. So what do I need to do to “clean” the sound after I used it? For the moment, I put the loading and the playing in commentary.

I also delete the collider and for the moment, it seems to be good! It still get some memory gain (like a couple of k) but I think that when I will have apply these change to my whole program it will be ok! I just completly forgot about the collider (I had the function ready in my collision system but never used it!)

So my only question is what is you proposition for the sound?

Thank you so much again!

Jaff

Just another question (sorry to double post).

I noticed that when I reduced my windows (I’m with windows XP), the memory usage drops A LOT!! Like when I’m in the main menu of my game, it use 130 mem even if nothing is going on. The reason is that the intro BEFORE the menu takes a lot of ressource. In the normal behavior, the memory will go back to 100 meg before I start a game. But if I reduce the window, wow, drops to 10 meg!!!

So my second question is: Is there a way to call the garbage collector at some moment. Or maybe call the function that is called during a “reduce” event.

I dont want to use this all the time but at strategic moment like the menu, new game, etc…

Thanks!

Jaff

Minimizing the window doesn’t really reduce your memory usage, it just swaps it all out to disk, so that most of your program is no longer resident. But it’s still consuming the same amount of virtual address space.

Assuming you’re using the Task Manager to monitor your memory usage, you probably want to enable the column “VM Size” instead of “Mem Usage”. “VM Size” stands for “Virtual Memory Size” and is a measure of the true memory footprint of your application, as opposed to the amount memory that just happens to be resident right now. (This is what the columns are named in XP. I think they’re named something different in Vista.)

Consider using the memory trackers built into PStats too.

David

It sounds like there’s a bug in the new audio code. I doubt there’s anything you can do about it, I just have to fix the bug.

As a temporary workaround, though, you could use fmod_audio instead of openal_audio. That’s been around longer and probably has fewer bugs.

I’ll look into the bug as soon as I’m at my office tomorrow.

  • Josh

Yes the current openAL code take way to much resources to function! I did not see it leak though. I could help with finding the slowdowns sense I would do that any ways. One is in python the other one is in c++. The python one could be eliminated by disabling Doppler effect.

garr started out as one post turned into 2 continue:
discourse.panda3d.org/viewtopic.php?p=20810

Hi guys,

Well, I’m already in FMOD because I started my project in a previous version of Panda3D and when I changed for the new one (with openAL activated), I got a lot of bug (Pop and cracks).

So I turn it to FMOD in the config file. But, why does the sound keep taking memory space when the objet using the sound is deleted?

I’ll do some test today and let you know.

Jaff

Wait… so you’re telling me the memory leak is in fmod, not openal?

Hi!

First, I don’t want to alarm anybody. I’m pretty sure the issue is related to my newbie talent with Panda3D.

What I’m sure:
-I use FMOD instead of OpenAL because I had problem with OpenAL.
-Each time I use an objet with sound, the memory usage increase of a small amount (normal) but never decrease (not normal). After a while, the memory usage of my game is so big that the game crash.

So my question is:
-Is it possible to delete a sound from the memory (like a node)
-Is there a way to get over the problem. I’m thinking of creating one class with only one instance (singleton) that will load all sound one time only and that will have method to start the different sound when needed. The bad thing with this is that all my current class will need to have a reference to this class and that will need some rethinking.

Because right now, each object when created loads the sounds they need, and play it when needed.

Is it me doing something wrong? Or problem with Panda3D. I don’t know! I hope you can help me Josh!

Thanks!

Jaff

In actuality, nodes and sounds both delete themselves automatically when you’re not using them any more.

Of course, a node that’s in the scene graph is being used, so you have to remove a node from the scene graph. But removal isn’t the same as deletion. You can remove something from the scene graph and then put it back in.

So what I’m saying is, garbage collection is automatic. If it’s not working, it’s a bug. It could be a bug in your program - maybe the sound is stored somewhere - but from what I saw of your code, I don’t think so. I think it’s a bug in panda.

If you’re using fmod, then that suggests a bug in fmod. I’m not going to try to fix a bug in fmod - that’s an old sound library that I’m trying to get rid of because the code is a disaster area. Instead, I suggest you switch to openal. If that causes problems for you, then let’s fix those problems.

Hi Josh and thanks for your patience!

I will follow your advice and switch my game to OpenAL during this week. I know I just need to change the config file but I want to test everything and see what is happening with the memery and the pop and crack.

I will also need to download the new version of Panda to be sure that I’m up to date.

I’ll keep you informed in this thread!
Thanks a lot!

Jaff

Hi Josh,

I just downloaded Panda 1.5.0 and everything is running with OpenAL.
Like I said before, I still got “pops and crack” some time. It’s highly related to loading and to the first minute of play. Also, when a new model is loaded or that a lot of enemy are in the screen at the same time, I got some crack. But after a while it seems to run smootly.

Maybe this has something to do with a memory buffer or something? Anyway, it can be endured since it’s not a professional game but I think this should be fixed because I think music and sound is a very very important part of a game.

Oh I almost forgot but the nice parts is that there is no memory leak with sound anymore!!! YEAH!!!

So we can close this thread but you can put a new bug on the list :confused:

Thanks!

Jaff

OK, it seems likely that this is related to buffering. Every time through the panda main loop, it makes sure that the sound system has 3 seconds worth of audio data buffered. If not, it reads a little more audio data from the sounds file and adds it to the buffer.

If at any point, the main loop stalls for more than 3 seconds, the buffer will run out and you’ll get a pop. This is most likely to happen when loading a whole bunch of models all at once, or a big model in egg format.

You can increase the amount of buffering by putting this in your config file:

audio-buffering-seconds 10

See if that helps.

Hi josh,

I tried it but it does not work… In fact, maybe it works for later game event (like when the game is started for a couple of minute and then a big model or a lot of ennemies appears at the same) because I think i didn’t get pops and crack after the game was started.

But for the first minute of play, I get “cracks” all the time… And they happened approximatly at the same place. What I feel is that the buffer need a couple of time before optimising his memory, but after a while, everything runs smootly…

What I will do during the weekend is let my tester use my game and see if he gets the same bugs in his computer. I will also try it on 2 other computers! I will let you know.

If you have any other idea, let me know!

Jaff

Josh Yelon, wow you just solved my other mystery! If i disable the c++ slow part. The sound plays but only for 3 seconds. If i start a new sound it plays all sounds for 3 sec. I even tried only calling the slow part once every one second but that did not work very well. I think making sure that the buffers have their 3 sec worth of audio is extremely slow.

Hi Josh,

During the weekend, we tested my game with 3 computers and with OpenAL it’s REALY REALY slow. That’s the reason why the sound cracks… I don’t know if there is a way to optimise this but I was able to play on my old computer and now I can’t.

And because of this, I have strage behavior sometime because the sound slow down everything. When I put back FMOD, everything is ok but I got the memory leak…

I would suggest to check and test OpenAL very seriously.

Please keep me updated.

Jaff

I have a theory. If the sound files are large enough, OpenAL will automatically stream them from disk. If you were to try to play several sounds simultaneously, streaming them all from disk, it would really chug.

Unfortunately, I forgot to give you any means to explicitly override this behavior. I think we can count this as a bug in 1.5.0. I’m going to make this a high-priority fix for 1.5.1.

But, just to make sure, let’s find out if that’s really the problem: How big are your sound files?

Hi and thanks for your answer.

I have a big sound file (for the music) and a lot of other sound file playing over depending what the player is doing. It depend what you want to say with “big”. My music is a normal .mp3 of 4 meg and the other sounds are normally .wav of average 500k.

I’m happy to see that this bug will be fixed!

Don’t hesitate to ask question. I can also give you my game if you want to test with it.

Thanks

Jaff