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 :
-
when removing a group with buttons or objects with event listeners, all event listeners in the group get removed?
-
Does this also account for a group in a group in a group etc. ?
-
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
-
Yes
-
Yes
-
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
â 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:
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)
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
old habits are hard to break 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 and if it is present we just remove it and print to the console if the debuger is on
Nice little trick for debuging