[Guide] Finding/Solving Memory Leaks

Do you have to use variables for timers and transitions, or do they nil out on their own? [import]uid: 147322 topic_id: 26628 reply_id: 142073[/import]

Hi everyone.

Fighting memory leaks in my game I found one strange thing:
Corona itself allocates about 10…20 kb every second (exact numbers can differ depending on the platform)
10 kb in case of empty project (only main.lua)
20 kb, if config.lua is also present (with fps = 60)

Proof:
main.lua:

[lua]local t = {}

t.showMemoryUsage = function()

local textureMem = system.getInfo(“textureMemoryUsed”)

local mem = collectgarbage(“count”)

print(“Texture mem: " … (textureMem / 1000) … " kb;\tmem: " … mem  … " kb”)

end

t.startMemoryTracking = function(period, forceCollect)

t.memTrackTimer = timer.performWithDelay(period, function()

if (forceCollect) then

collectgarbage(“collect”)

end

t.showMemoryUsage()

end, -1)

end

collectgarbage(“stop”)

t.startMemoryTracking(1000, false)

[/lua]

You can change period from 5000 and play with ‘forceCollect’ to see that there are really 20 kb. Yes, they are cleaned up by gc, but that’s not good anyway.

Can someone from Corona staff explain this?

Hi!

Does this mean :

  1. when removing a group with buttons or objects with event listeners, all event listeners in the group get removed?

  2. Does this also account for a group in a group in a group etc. ?

  3. And if so, when using storyboard and the (multi layerered) objectsGroup is inserted in the storyboard group, nothing needs to be removed or nilled?

Thx

  1. Yes

  2. Yes

  3. Yes, if its attached to an object that has been put into the storyboard’s scene.view (aka “group”).  There are things that still have to be hand removed which includes:

  • Runtime listeners
  • Audio
  • Timers
  • Transitions
  • And stopping anything with an active call back  running (like doing a network.download and leaving the scene before the download finishes)

when we say Runtime listeners, we are talking about the ones where you do:

      Runtime :addEventListener(“someevent”, somefunction)

not:

   myButton :addEventListener(“someevent”, somefunction)

If myButton is in the scene.view, it will be taken care of.

I’m having a HUUUGE memory leak … I’m cleaning every local variable in my code and I can free about 20 ~ 30 of memory! And its using about 600 bytes of memory in my scene 

Using the profiller tool in mode 4 it shows me that the variables that are using most of memory are those of the array of the spritesheet, the ones generated by the function newSpriteSheetFromData(). I’m removing the spriteSheet with dispose() , but even so the memory isn’t cleaned … there is anyway of cleaning this array?

Another question … i’m using “module (…, package.seeall)” to create classes and to assess variables in this classes i’m using set and get methods. The profiller tells me that some of this gets and setters are consuming a considerable amount of memory! There’s any way to clean the memory associated with this functions? 

Thanks for the help! 

Do you set your spritesheet to nil after disposing it?

For the 2nd question we probably need to see your code to see what you’re setting.

Yes I’m setting it to nil 

the functions that I say are something like this:

function playerImage:playAnimation(animation) if(canPlayAnimation) then playerImage:prepare(animation) playerImage:play() canPlayAnimation = false end end function playerImage:getIsShoting() return isShoting end function playerImage:setIsShoting(value) isShoting = value end function playerImage:getShotType() return typeOfShot end function playerImage:setShotType(value) typeOfShot = value end function playerImage:getHP() return HP end

thanks in advance!! 

With your option 6 what if the object is required in an array for example, do you need to return?

Local newEnemy = {}
For i = 1, 10 do
NewEneny = display.rect etcetcetc
Return newEnemy
End

Yay, nay??

Hi Edulucats
Remember that the pacage see all is deprecated (think its main reason was mem leakage) You should read up on classes with public and private methods instead like this:

–edulucatsPlayerGenerator.lua

ECC = {} --no fancy just EduluCatsClass :slight_smile:
– all the chunks of code goes here like i.e:

ECC.returnSomething = function(someVar)
return someVar
end

return ECC

–in i.e. your main.lua—
require “edulucatsPlayerGenerator”

–or with a handle:
local ECClass = require (“edulucatsPlayerGenerator”)

Remember that there are times and situations that you need to use global vars when dealing with external libs/classes and it cost a littlebit more mem and theorretically its slower…ok, it is slower LOL
(I write on ckrappy tablet keyb so dont pay attention to typos

Do we still need something like cleangroups.lua ? http://cl.ly/58zl

There’s are a lot of outdated “tricks” and “utilities” and I just wanted to double check.
 

Great topic, didn’t know about returning display objects within functions, will comb through my tons of lines of code and see if this fixes my memory leak.

Thanks for the info.

I made a post about some doubts I had about memory management, but I didn’t receive any answer up till now!

I’ll just copy and paste it in this post since this thread seems very active on the argument.

Here we go:


Hello everybody,

 

I am wondering for a while about what really happens in terms of memory when I remove and set to nil groups and display objects.

 

I will use some examples to clarify my questions:

 

Case 1:

local myGroup = display.newGroup() local rect = display.newRect(0,0,100,100) myGroup:insert(rect) myGroup:removeSelf() myGroup = nil

Does removing and setting to nil ALSO frees from memory the rectangle or do I need to set rect to nil before removing the group?

 

Case 2:

local rect = display.newRect(0,0,100,100) rect.myProperty = "myProperty" rect:removeSelf() rect = nil

Does removing my rect ALSO frees from memory rect.myProperty? If yes, is this behaviour valid even if I add a table as a property (and all its elements, wich could also have some custom properties)?

 

Case 3:

local myGroup = display.newGroup() local rect = display.newRect(0,0,100,100) rect.myProperty = "myProperty" myGroup:insert(rect) myGroup:removeSelf() myGroup = nil

Am I freeing from memory the rectangle AND its custom property myProperty?

 

Briefly, can anyone give me a solid answer on how to prevent memory leaks in my Corona projects? Reading the Corona Guides didn’t help me, the more I read, the more I get confused!

 

I would also appreciate some common practice to manage memory in a clean way!

 

Thank you in advance! 


That’s not valid syntax.  Is this “real” code, or maybe typo’d on-the-fly?  Assuming you meant myGroup = nil, then…

Case 1:  removing a group from the display will also remove its children from the display.  However, you still have a lingering reference to that display object stored in “rect” which you did not nil – so it will linger/leak.

Case 2:  yes, as long as  none of your additional properties “in” the object are still referenced somewhere ELSE (that is, the entire object is unreferenced) then when you release the object it will also release everything inside of it (fe “myProperty”)

Case 3:  suffers from exact same problem as Case 1, and again rect will linger/leak.  if you ALSO add “rect = nil” then the rect (and its additional properties as per Case 2) will be released

@dabebollinger: thank you very much. This was hauntig me for a while now!

I’ll assume that Composer behaves the same way, and when destroying a scene (wich is basically a displayGroup) I need to nil variables anyway!

The best place for it should be the scene:destroy event, right?

PS: myGroup:nil was crearly a typo, sorry! Edited the previous post!

right - composer/storyboard will remove the scene’s group (aka it’s “view”, and thus also removing its children from the display), but if you have any local references to those children you’ll still want to nil those during destroy().

To answer Q-tro and anyone else confused about how to manage your memory, I learned the hard way and spent many weeks on this issue, my game was leaking 100k between every scene change and at the end of the first world I had a massive 1.5mb leak.

Going with Q-tro first.

Variables do not need to be nilled, just make sure they are all local.

Make sure to insert all your display objects into your scenes main group.

function scene:enterScene(event) local mainGroup = self.view local gameOptions = display.newImageRect("images/gameOptions.png", 300, 300) mainGroup:insert( gameOptions )

If you create a group of objects don’t forget to insert that group into your scenes main group.

function scene:enterScene(event) local mainGroup = self.view local gameOptions = display.newImageRect("images/gameOptions.png", 300, 300) backgroundGroup:insert(gameOptions) mainGroup:insert( backgroundGroup )

So everything you insert into your group and scenes main group you don’t have to worry about, it’s all taken care of, but…

Look out for handles, the handles must be nilled before the objects they are attached to are removed.

local GUI = display.newGroup() local gameBackground = display.newImageRect("images/backdrop.png", 1425, 900) GUI.backdrop = gameBackground -- backdrop is the handle mainGroup:insert(GUI) Remove like this: GUI.backdrop = nil -- Remove the handle first display.remove(gameBackground) -- Then remove the display object because we didn't insert it into GUI we only assigned a handle to it.

GUI is inserted into the scenes main group so it will be removed for you and so will every object you INSERT into GUI.

Don’t worry about event listeners attached to objects if the objects are inserted into the scenes main group, they are definitely taken care of for you, I’ve tested it.

backGround:addEventListener( "touch", showMenu ) mainGroup:insert(backGround)

Do the above and you can forget about the backGround object and the event listener attached to it.

All Runtime event listeners you must remove yourself.

Runtime:removeEventListener( "enterFrame", handleEnterFrame )

Never create text or display objects inside a function, create them at the top of your code and alter their properties later.

For example, there is nothing stopping you creating a blank text object to begin with and adding to it’s properties in your functions later on if you don’t know what they will be yet.

local options = { text = "", x = 0, y = 0, width = 0, height = 0, fontSize = 30, font = "Helvetica", align = "center" } local name = display.newText( options ) name.isVisible = false mainGroup:insert(name) options = nil

Then alter it’s properties in a function.

local createCharacter = function() name.text = "Billy" name.x = 500 name.y = 500 name.isVisible = true end

Be extra careful with timers and transitions, especially if you use a lot of them, I made this mistake.

Local myTimer myTimer = timer.performWithDelay( 1000, showHighScores, 1 ) myTimer = timer.performWithDelay( 2000, removeMenu, 1 ) myTimer = timer.performWithDelay( 4000, playGame, 1 )

You have many instances of MyTimer, how do you nil them? You can’t.

Instead do this

local myTimer = {} local timerCount = 1 myTimer[timerCount] = timer.performWithDelay( 1000, showHighScores, 1 ) timerCount = timerCount + 1 myTimer[timerCount] = timer.performWithDelay( 2000, removeMenu, 1 ) timerCount = timerCount + 1 myTimer[timerCount] = timer.performWithDelay( 4000, playGame, 1 ) timerCount = timerCount + 1

Then when you want to nil your timers (use the same method with transitions by the way)

for i = 1, timerCount do myTimer[i] = nil end myTimer = nil

The last thing is audio, audio has to be removed by you. Keep it simple.

audio.reserveChannels( 3 ) local backSound = audio.loadSound( "sounds/inspiration.ogg" ) local holdSound = audio.loadSound( "sounds/hold.ogg" ) local clickSound = audio.loadSound( "sounds/click.ogg" )

Play it, don’t bother with handles.

audio.play(backSound, {channel = 1, loops=-1}) -- To stop the backSound playing anywhere.. audio.stop(1) -- To remove your audio always do this sequence.. audio.stop() -- Makes sure all your audio is stopped first audio.dispose(backSound) backSound = nil audio.dispose(holdSound) holdSound = nil audio.dispose(clickSound) clickSound = nil

Clean and simple.

Last but not least clean up everything you require…

local GUI = require("GUI") local loadsave = require("loadsave") Get rid of it.. package.loaded["loadsave"] = nil package.loaded["GUI"] = nil loadsave = nil GUI = nil

In all my modules I create a clean up function that I call just before gotoScene(), it’s important to clean up in the right order…

local cleanUP = function() audio.stop() audio.dispose(backSound) audio.dispose(clickSound) audio.dispose(holdSound) backSound = nil clickSound = nil holdSound = nil for i = 1, timerCount do if myTimer[i] ~= nil then timer.cancel( myTimer[i] ) myTimer[i] = nil end end myTimer=nil for i = 1, transCount do if myTransition[i] ~= nil then transition.cancel( myTransition[i] ) myTransition[i] = nil end end myTransition = nil GUI.backdrop = nil (nil that handle!) package.loaded["loadsave"] = nil package.loaded["GUI"] = nil loadsave = nil GUI = nil end

As long as you declare all your variables local and insert all your display objects into your scenes group then this is all you need to clean up.

Something that has me perplexed is why do we have to declare EVERYTHING local?

Would it make more sense to do away with it and have a Global declaration instead?

Considering how little (or not at all) you use globals?

Looking at one of my modules with 2000 lines of code all I see is

local

local

local

local

… and so on over and over…

Food for thought :huh:

@QuizMaster

AMEN!

This is really good stuff. I also did some hundred hours of debuging before I came to the same results. One thing you could do is if you have many variables for lets say audio, is to put them all in a table so you can iterate over them and stop them, remove them and nil them out. One  other thing is if you are sick of writing “local” over and over again like:

[lua]

local this

local that

[/lua]

you can of course just write:

[lua]
local this, that
[/lua]

You get fewer lines too

I know you know all of this QuizMaster but there are others here who dont ;D

What you wrote over here will save newbies hundred of hours debuging for shure

One other thing is the forward declaration of variables prohibit strange bugs from appearing (fwd declaration means that you declare the variables in the start of your script and then , later in your code they acts like handles for objects…this is also known as “visible to all scopes in the script”)

Thanx Quizmaster for the goodstuff

Thanks for the thumbs up Hendrix, and good a tip with the audio.

Yeh I know about local this, that …

But when you have a couple thousand lines of code in a single module I find it better for debugging to put the local with each object I create, that way you’re not going back and forth through your code to make sure you’ve declared it. Never the less when my code is finished I will definitely put all my locals in single lines like you pointed out.

That’s the beauty of Corona the possibilities are endless.

As I mentioned, I learned the hard way, hopefully what I’ve learned on here on the forums can save someone else the anguish I went through.

The Corona Staff and the community here have been so generous with their advice I felt I had to give something back, something you won’t find in the docs that’s why I took a couple of hours out my day to make that long post.

Martin. (The Quiz Master)

@Martin

One more thing…

I totally agree with the declaring globals as globals when needed, instead of declaring locals that should be a default value like i.e anchor points are default 0.5 (center) and not left or right, but I guess there are reasons for that too.

Imagine the frustration IF THEY DID change locals to default, we would write: loc… damn I dont need to write that any more LOL :slight_smile:

old habits are hard to break Martin

@Martin

Based on you clever notes regarding removing classes/routines I did a little tweak for my own use and it helped in my debuging process. I have a switch in my main file I call: _G.debug = true/false (Now, in debug mode I have it set to true …- who could have guessed ;D)

In my reset file: fail.lua (that is called when you choose wrong) I do the following (for this example I have a class required called score:

[lua]

if package.loaded.score then

package.loaded[“score”] = nil

if _G.debug == true then print( “Removed class: score” )  end

end

[/lua]

The console writes:

Mar 23 23:12:19.775: Removed class: score

I pack up the un-require of the class in a conditional if-clause because if it is removed it can be hard to remove it :wink: and if it is present we just remove it and print to the console if the debuger is on

Nice little trick for debuging