Jump to content

[TOPIC: topicViewTemplate]
[GLOBAL: userSmallPhoto]
Photo

[Guide] Finding/Solving Memory Leaks
Started by Danny May 21 2012 12:27 AM

* * * * * 3 votes
72 replies to this topic
[TOPIC CONTROLS]
Page 2 of 3 1 2 3
[/TOPIC CONTROLS]
[modOptionsDropdown]
[/modOptionsDropdown]
[reputationFilter]
[TOPIC: post.html]
#26

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 26,205 posts
  • Enterprise

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.



[TOPIC: post.html]
#27

edulucats0

[GLOBAL: userInfoPane.html]
edulucats0
  • Observer

  • 13 posts
  • Corona SDK

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!! 



[TOPIC: post.html]
#28

jazzarssoul

[GLOBAL: userInfoPane.html]
jazzarssoul
  • Enthusiast

  • 56 posts
  • Corona SDK

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??

[TOPIC: post.html]
#29

Hendrix000007

[GLOBAL: userInfoPane.html]
Hendrix000007
  • Contributor

  • 177 posts
  • Corona SDK

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

[TOPIC: post.html]
#30

jack95

[GLOBAL: userInfoPane.html]
jack95
  • Contributor

  • 197 posts
  • Corona SDK

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.
 



[TOPIC: post.html]
#31

QuizMaster

[GLOBAL: userInfoPane.html]
QuizMaster
  • Contributor

  • 235 posts
  • Corona SDK

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.



[TOPIC: post.html]
#32

Q-tro

[GLOBAL: userInfoPane.html]
Q-tro
  • Observer

  • 16 posts
  • Corona SDK

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! 

--------------


[TOPIC: post.html]
#33

davebollinger

[GLOBAL: userInfoPane.html]
davebollinger
  • Corona Geek

  • 1,373 posts
  • Corona SDK

myGroup:nil

 

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



[TOPIC: post.html]
#34

Q-tro

[GLOBAL: userInfoPane.html]
Q-tro
  • Observer

  • 16 posts
  • Corona SDK

@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!



[TOPIC: post.html]
#35

davebollinger

[GLOBAL: userInfoPane.html]
davebollinger
  • Corona Geek

  • 1,373 posts
  • Corona SDK

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().



[TOPIC: post.html]
#36

QuizMaster

[GLOBAL: userInfoPane.html]
QuizMaster
  • Contributor

  • 235 posts
  • Corona SDK

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:



[TOPIC: post.html]
#37

Hendrix000007

[GLOBAL: userInfoPane.html]
Hendrix000007
  • Contributor

  • 177 posts
  • Corona SDK

@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:

local this
local that

you can of course just write:

local this, that

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



[TOPIC: post.html]
#38

QuizMaster

[GLOBAL: userInfoPane.html]
QuizMaster
  • Contributor

  • 235 posts
  • Corona SDK

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)



[TOPIC: post.html]
#39

Hendrix000007

[GLOBAL: userInfoPane.html]
Hendrix000007
  • Contributor

  • 177 posts
  • Corona SDK

@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 :)

old habits are hard to break Martin



[TOPIC: post.html]
#40

Hendrix000007

[GLOBAL: userInfoPane.html]
Hendrix000007
  • Contributor

  • 177 posts
  • Corona SDK

@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:

if package.loaded.score then
package.loaded["score"] = nil
if _G.debug == true then print( "Removed class: score" )  end
end

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



[TOPIC: post.html]
#41

QuizMaster

[GLOBAL: userInfoPane.html]
QuizMaster
  • Contributor

  • 235 posts
  • Corona SDK

Exactly what I was trying to say, local should be done away with, everything should be local as default and if you want something to be Global then declare it as such. It wouldn't help us now but it sure would help a lot of newbies from making mistakes.

 

Very interesting Hendrix, thanks for the debugging tip.

 

Happy coding.



[TOPIC: post.html]
#42

pasidaips

[GLOBAL: userInfoPane.html]
pasidaips
  • Observer

  • 12 posts
  • Corona SDK

it can be as easy as adding one more variable in build settings like 
 

reverseGlobalsLocals =
{
-- Include only the necessary icon files on each platform
announceGlobals = true
},

meaning that for those that got older projects they can still keep their projects without editing billions of lines, and new people get the option of using the global instead of local like it is now


[TOPIC: post.html]
#43

QuizMaster

[GLOBAL: userInfoPane.html]
QuizMaster
  • Contributor

  • 235 posts
  • Corona SDK

@pasidaips

 

Can you elaborate more on this, I find it intriguing, never heard of this, "announceGlobals = true"????

 

Is there any more knowledge you have about the build settings that could save us all a lot more work?

 

I appreciate your input!

 

Martin.



[TOPIC: post.html]
#44

pasidaips

[GLOBAL: userInfoPane.html]
pasidaips
  • Observer

  • 12 posts
  • Corona SDK

settings =
{


orientation =
{
-- Supported values for orientation:
-- portrait, portraitUpsideDown, landscapeLeft, landscapeRight


default = "portrait",
supported = { "portrait", "portraitUpsideDown" },
--"landscapeLeft", "landscapeRight",


},

sry seems i didn't write it well enough, i was just giving idea to developers how they COULD implement it easily so that both the old and new developers can keep either local variableName style or global variableName by switching them with a setting inside build.settings

announceGlobals does not exist as such and was given as an example so sorry for miseading you and giving false hope :-(



 


[TOPIC: post.html]
#45

QuizMaster

[GLOBAL: userInfoPane.html]
QuizMaster
  • Contributor

  • 235 posts
  • Corona SDK

I couldn't agree more.

 

Am I the only one who thinks that declaring everything local seems like a pointless exercise?

 

There is a lot of silence coming from the Corona team on this subject, would it be a huge undertaking to fix this issue?

 

I eagerly await responses...



[TOPIC: post.html]
#46

davebollinger

[GLOBAL: userInfoPane.html]
davebollinger
  • Corona Geek

  • 1,373 posts
  • Corona SDK

 

There is a lot of silence coming from the Corona team on this subject, would it be a huge undertaking to fix this issue?

 

 

Yes, huge, google it, long-debated -- easier to stop thinking its broke, because then it doesn't need to be fixed.  ;)

 

Local-by-default only sounds "better" until you do some functional -style programming, and try to reference a nested outer scope --  that is, something not local, yet not global either -- then you'd see how confounding it would be to access those "middle" scopes.



[TOPIC: post.html]
#47

Hendrix000007

[GLOBAL: userInfoPane.html]
Hendrix000007
  • Contributor

  • 177 posts
  • Corona SDK

"external local variables" or upvalues. Yes there are some subtleties in this matter

In other languages it is like you say QuizMaster, default to locals unless you declare it as global

Interesting reading here:

http://www.lua.org/pil/6.1.html



[TOPIC: post.html]
#48

QuizMaster

[GLOBAL: userInfoPane.html]
QuizMaster
  • Contributor

  • 235 posts
  • Corona SDK

@Dave, yes that makes perfect sense but I'm not talking about functions here, we all know that something inside a function is local to that function unless you declare the variable local outside of it.

 

I'm talking about everything else, looking through my code it's local this, local that, local, local local..... seems crazy.

 

@Hendrix, yes I read that when I first started learning LUA to get a grasp in the language to help me with Corona, I found it very confusing, thank god Corona is a lot easier to understand.

 

I bet this is a bigger subject than I originally thought it was, I'm going to do some reading and learning.

 

Happy coding everyone.



[TOPIC: post.html]
#49

Hendrix000007

[GLOBAL: userInfoPane.html]
Hendrix000007
  • Contributor

  • 177 posts
  • Corona SDK

I agree to that QuizMaster

Many potential issues tends to solve themselves if we just forward declare the locals in the start of the code due to lexical scoping :)



[TOPIC: post.html]
#50

kermit4karate

[GLOBAL: userInfoPane.html]
kermit4karate
  • Enthusiast

  • 42 posts
  • Corona SDK

What a great post. I really, really should have read this before I started my app, but live and learn. 




[topic_controls]
Page 2 of 3 1 2 3
 
[/topic_controls]