Memory leak

hi,

I wrote a function which I thought I had eliminated all the memory leaks in.

But when I test the garbagecollect(“count”), I see a bit a of a memory leak going on…

the function adds a physics object to the screen, and checks to see if the player has managed to hit it.

I’m posting only part of the code for it because it’s too long, but this part has the cleanup in it so I think that’s where the problem is.

Can someone take a look at the code and see if i’ve missed something please?

local function suck(obj)

        local good=display.newImage(“Photos/good.png”)

        good.x=obj.x; good.y=obj.y

        ball.ballX(obj)

        local trans2=transition.to(good, {time=100, xScale=2,yScale=2})

        local trans=transition.to(obj, {time=250, width=0,height=0,x=obj.x, y=obj.y,alpha=0})

        timer.performWithDelay(300,function()

        if isPlaying then

        trans=nil;good:removeSelf(); good=nil;trans2=nil

        end

        end)

    end

local function loop(event)

         for i = #t, 1, -1 do

            local object = t[i]

            

                if(object~=nil) then

                

                            if(isPaused) then

                            object.isVisible=false

                            else

                            object.isVisible=true

                            end

                            

                                if(object.y~=nil) and object.y>700 then

                                    score.setScore(-20)

                                    local child = table.remove(t, i) 

                                            if child ~= nil then

                                            child:removeSelf()

                                            child = nil

                                            check=nil

                                            isPopped=nil

                                            px=nil;pl=nil;rotate=nil

                                            suck=nil

                                            reventar=nil

                                            crate=nil

                                            Runtime:removeEventListener(“enterFrame”, loop)

                                            Runtime:removeEventListener(“collision”,reventar)

                                            loop=nil

                                            end

                    

                                 elseif(object.fastkill) then

                                    score.setScore(100)

                                    isPopped=isPopped+1

                                    timer.performWithDelay(isPopped*50,function()

                                        local popSound = audio.play( pop )

                                            isPopped=0

                                        end)

                                    ball.addOne()

                                    local popSound = audio.play( pop )

                                    suck(object)

                                    local child = table.remove(t, i) 

                                    local timeCrate=timer.performWithDelay(300,function()

                                            if child ~= nil and isPlaying then

                                                child:removeSelf()

                                                child = nil

                                                check=nil

                                                ballAdd=nil

                                                isPopped=nil

                                                isPopped=nil

                                                suck=nil

                                                reventar=nil

                                                crate=nil

                                                px=nil;pl=nil;rotate=nil

                                                Runtime:removeEventListener(“enterFrame”, loop)

                                                Runtime:removeEventListener(“collision”,reventar)                                                loop=nil

                                                timeCrate=nil

                                            end

                                        end)

                                end

                end

        end

end

    

for i=1,#t do

        t[i].id = i

    end

Runtime:addEventListener(“enterFrame”, loop)

Runtime:addEventListener(“collision”,reventar)

** LOOKED AT CODE SEE LATER COMMENTS **

@danlinenberg,

I haven’t looked at your code yet, but don’t be so sure you have a leak.  Sometimes it looks like you’ve got a leak when in actuality, all you’ve get is a cyclic allocation and de-allocation going on.

When you use collectgarbage(“count”) to look for a leak, you want to call it like this:

collectgarbage() -- Force collection local val = collectgarbage("count") -- Show true in use value print("In use:", val ) 

M.Y. Developers has a great profiler (to find leaks and performance issues)

As well, I have a nice tool for visually examining FPS/Mem/Vid Mem/More. (GumRoad/Sellfy).

I just recently wrote this (messy) bit of code to test an idea I had.  It will print to the console every time if sees a new ‘low’ memory or ‘high’ memory usage.  If the high-memory keeps creeping up (after 30…45 seconds), you’ve got a leak.  Note: This is not as good as the profiler by MY Developers nor my RG Super Meter.  However, it is free:

-- == -- &nbsp; &nbsp;round(val, n) - Rounds a number to the nearest decimal places. (http://lua-users.org/wiki/FormattingNumbers) -- &nbsp; &nbsp;val - The value to round. -- &nbsp; &nbsp;n - Number of decimal places to round to. -- == local function round(val, n) &nbsp; if (n) then &nbsp; &nbsp; return math.floor( (val \* 10^n) + 0.5) / (10^n) &nbsp; else &nbsp; &nbsp; return math.floor(val+0.5) &nbsp; end end local low = 99999999 local last = 0 local high = 0 local val = 0 local reportedHigh = false local highChanged = false local function onEnterFrame( ) collectgarbage() &nbsp;&nbsp;&nbsp;&nbsp;val = collectgarbage("count") &nbsp;&nbsp;&nbsp;&nbsp;if(val \< low) then&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;highChanged = true &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;low = val &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print("New low: ", &nbsp;round(low,2) ) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;--print(round(low,2)) &nbsp;&nbsp;&nbsp;&nbsp;end &nbsp;&nbsp;&nbsp;&nbsp;if( val \> high ) then &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;highChanged = true &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;high = val &nbsp;&nbsp;&nbsp;&nbsp;end &nbsp;&nbsp;&nbsp;&nbsp;if(last \> val and not reportedHigh and highChanged) then &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print("Recent high: ", round(high,2) ) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;--print(round(high,2)) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reportedHigh = true&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;elseif(last \< val and reportedHigh) then &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reportedHigh = false &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;highChanged = false&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;end &nbsp;&nbsp;&nbsp;&nbsp;last = val&nbsp;&nbsp;&nbsp;&nbsp; end Runtime:addEventListener( "enterFrame", onEnterFrame )&nbsp;

Now, after all that I will look at your code more closely and write back if I see something.  However, you might simultaneously be trying one of the above code snippets or tools to debug further.

-Ed

@danlinenberg,

OK.  Now I’ve looked at your code.  I don’t see a specific issue that could cause a leak, but I do see some things you can simplify and not do which may help you find the problem.

1. Variable with same name a function?

It  looks like you’ve created a variable in your code called suck and a function suck?  Either that, or you are trying to destroy the function by setting it to nil?

If it is the prior, rename that variable to something else.  Be careful not to have variables with the same name as functions. It is confusing an dangerous.

If it is the latter, that won’t work.  You don’t free up memory by setting the name of a function to nil.  Let Lua worry about whether a function is in scope/use and can be removed.

2. Don’t go nil crazy.  

You seem to be setting a lot of variables to nil.  Only nil these types of variables:

  • Globals that refer to objects after you have called removeSelf() on that object

  • File level locals that refer to objects after you have called removeSelf() on that object

You do not need to nil local variables within loops or within functions.  These locals will automatically be cleaned up after the local goes out of scope.  That said, you may need to set these locals to nil if you are using them in logical comparisons, where ‘nil’ is an expected value.

3. Minimize the use of globals and file level locals.  Also, if you can avoid it, don’t create locals in functions and loops if you ** don’t need them.**

I often see people create lots of local variables that they use in collaboration with an object like this:

local bob = display:newImageRect( args &nbsp;here ) local bobHeight = 60 local bobWeight = 180 local bobAge = 32 local sue= display:newImageRect(&nbsp;args &nbsp;here&nbsp;) local sueHeight = 60 local sueWeight = 180 local sueAge = 32 local bobFriend = sue local sueFriend = bob&nbsp;

Then later they do this:

bob:removeSelf() bob = nil bobHeight = nil bobWeight = nil bobAge = nil sue:removeSelf() sue = nil sueHeight = nil sueWeight =&nbsp;nil sueAge = nil bobFriend = nil sueFriend = nil

Not only is this ‘nil-crazy’, but a huge waste of space and time.  They should do something like this instead:

local bob&nbsp;= display:newImageRect(&nbsp;args &nbsp;here&nbsp;) bob.myHeight = 60 bob.myWeight = 180 bob.myAge = 32 local sue= display:newImageRect(&nbsp;args &nbsp;here&nbsp;) sue.myHeight = 60 sue.myWeight = 180 sue.myAge = 32 bob.myFriend = sue sue.myFriend = bob&nbsp;

Now, cleanup is easy:

bob:removeSelf() sue:removeSelf() bob = nil sue = nil&nbsp;

The moral of this story is use fields on objects instead of local or global variables when you can.  Just be cautious about field names (don’t use default field names) and about referencing one object from another. i.e. If we only wanted to remove bob, we’d do this:

bob:removeSelf() sue.myFriend = nil -- Clean up reference to bob bob = nil&nbsp;

I will continue to look at your code and see if I find any more things or the root of your troubles.

OK, I don’t see anything else.  Again, don’t be sure you’ve got a leak till you really inspect it.

MY Developer’s profiler is $10 and great for finding leaks.  Also, my tool ($5) will give you a clear view of your apps behavior:

http://www.youtube.com/watch?v=XIpZ3mQsbz4

Cheers,

Ed

leak.png

I’ve used your bit of code roaminggamer, 

And this “new high” keeps popping up every 20-30 sec. It pops up instantly if the “fastkill” part of the code is triggered.

As far as the nilling and variables - 

I’ve read somewhere that used local variables for math.random is better than simply using math.random, is that true?

and i’ve nilled everything because I was just clueless as to where the leak was coming from so I tried everything I could think of 

** FIXED TYPO/VERBAGE** 1805 PST

@danlinenberg,

Looks like we’re in different time zones or you are an early riser. :slight_smile:

If the ‘new high’ keeps printing and rising, then you have probably got a leak.  Please, note that the code above was written quickly and not sophisticated.  So I can only say that it looks like you have a leak.   

So, I do suggest the M.Y. Developers profiler which has a variable (global and local) analyzer to help find leaks.  It does take some work, but it will be worth it if you find and clean up a leak.

Re: Localizing math (and other) functions

A localized function is always faster than one in the global space or attached to the namespace of a library (like math).

There are three (general) levels of localization:

  1. File Level - The local is defined in a file outside the scope of a function. - FAST ; BESTin most cases
  2. Function Level - The local is defined in a function before it is used and outside any loop bodies. - FASTEST for functions that run large (100K+) loops.  Enven then, this is only minutely faster than file level.  
  3. Loop-Level - The local is defined in a loop, or some other scope modifier (not a function definition). - SLOW ;  This is slow because every time you get back to the top of the loop, you re-initialize the local variable holding the handle to the function.  This incurs a look-up cost which is what localization practices are meant to reduce.

If I were asked to vote I would say, always use File Level Localization.  See example below:

-- Top of file local mRand = math.random local mSqrt &nbsp;= math.sqrt local newCircle = display.newCircle -- ...&nbsp; local function doit() &nbsp; &nbsp; &nbsp; &nbsp;newCirlce( args ) &nbsp; &nbsp; &nbsp; local mysqr = mSqrt( 121 ) end -- End of File&nbsp;

Parting factoid:

I am currently working on a Corona SDK performance profiler (measuring performance of Corona and Lua builds).  In my tests so far, I have found that, 

mSqrt (as shown above) vs. math.sqrt (called directly) is 26% faster for same number of ops, or alternately capable of 1.36X as many ops in same time.

3.39 million ops/s vs 2.5 million ops/sec. 

Speedup == 1 - (2.5 / 3.39) ~= 0.26 (i.e. 26%)

** LOOKED AT CODE SEE LATER COMMENTS **

@danlinenberg,

I haven’t looked at your code yet, but don’t be so sure you have a leak.  Sometimes it looks like you’ve got a leak when in actuality, all you’ve get is a cyclic allocation and de-allocation going on.

When you use collectgarbage(“count”) to look for a leak, you want to call it like this:

collectgarbage() -- Force collection local val = collectgarbage("count") -- Show true in use value print("In use:", val )&nbsp;

M.Y. Developers has a great profiler (to find leaks and performance issues)

As well, I have a nice tool for visually examining FPS/Mem/Vid Mem/More. (GumRoad/Sellfy).

I just recently wrote this (messy) bit of code to test an idea I had.  It will print to the console every time if sees a new ‘low’ memory or ‘high’ memory usage.  If the high-memory keeps creeping up (after 30…45 seconds), you’ve got a leak.  Note: This is not as good as the profiler by MY Developers nor my RG Super Meter.  However, it is free:

-- == -- &nbsp; &nbsp;round(val, n) - Rounds a number to the nearest decimal places. (http://lua-users.org/wiki/FormattingNumbers) -- &nbsp; &nbsp;val - The value to round. -- &nbsp; &nbsp;n - Number of decimal places to round to. -- == local function round(val, n) &nbsp; if (n) then &nbsp; &nbsp; return math.floor( (val \* 10^n) + 0.5) / (10^n) &nbsp; else &nbsp; &nbsp; return math.floor(val+0.5) &nbsp; end end local low = 99999999 local last = 0 local high = 0 local val = 0 local reportedHigh = false local highChanged = false local function onEnterFrame( ) collectgarbage() &nbsp;&nbsp;&nbsp;&nbsp;val = collectgarbage("count") &nbsp;&nbsp;&nbsp;&nbsp;if(val \< low) then&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;highChanged = true &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;low = val &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print("New low: ", &nbsp;round(low,2) ) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;--print(round(low,2)) &nbsp;&nbsp;&nbsp;&nbsp;end &nbsp;&nbsp;&nbsp;&nbsp;if( val \> high ) then &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;highChanged = true &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;high = val &nbsp;&nbsp;&nbsp;&nbsp;end &nbsp;&nbsp;&nbsp;&nbsp;if(last \> val and not reportedHigh and highChanged) then &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print("Recent high: ", round(high,2) ) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;--print(round(high,2)) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reportedHigh = true&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;elseif(last \< val and reportedHigh) then &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reportedHigh = false &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;highChanged = false&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;end &nbsp;&nbsp;&nbsp;&nbsp;last = val&nbsp;&nbsp;&nbsp;&nbsp; end Runtime:addEventListener( "enterFrame", onEnterFrame )&nbsp;

Now, after all that I will look at your code more closely and write back if I see something.  However, you might simultaneously be trying one of the above code snippets or tools to debug further.

-Ed

@danlinenberg,

OK.  Now I’ve looked at your code.  I don’t see a specific issue that could cause a leak, but I do see some things you can simplify and not do which may help you find the problem.

1. Variable with same name a function?

It  looks like you’ve created a variable in your code called suck and a function suck?  Either that, or you are trying to destroy the function by setting it to nil?

If it is the prior, rename that variable to something else.  Be careful not to have variables with the same name as functions. It is confusing an dangerous.

If it is the latter, that won’t work.  You don’t free up memory by setting the name of a function to nil.  Let Lua worry about whether a function is in scope/use and can be removed.

2. Don’t go nil crazy.  

You seem to be setting a lot of variables to nil.  Only nil these types of variables:

  • Globals that refer to objects after you have called removeSelf() on that object

  • File level locals that refer to objects after you have called removeSelf() on that object

You do not need to nil local variables within loops or within functions.  These locals will automatically be cleaned up after the local goes out of scope.  That said, you may need to set these locals to nil if you are using them in logical comparisons, where ‘nil’ is an expected value.

3. Minimize the use of globals and file level locals.  Also, if you can avoid it, don’t create locals in functions and loops if you ** don’t need them.**

I often see people create lots of local variables that they use in collaboration with an object like this:

local bob = display:newImageRect( args &nbsp;here ) local bobHeight = 60 local bobWeight = 180 local bobAge = 32 local sue= display:newImageRect(&nbsp;args &nbsp;here&nbsp;) local sueHeight = 60 local sueWeight = 180 local sueAge = 32 local bobFriend = sue local sueFriend = bob&nbsp;

Then later they do this:

bob:removeSelf() bob = nil bobHeight = nil bobWeight = nil bobAge = nil sue:removeSelf() sue = nil sueHeight = nil sueWeight =&nbsp;nil sueAge = nil bobFriend = nil sueFriend = nil

Not only is this ‘nil-crazy’, but a huge waste of space and time.  They should do something like this instead:

local bob&nbsp;= display:newImageRect(&nbsp;args &nbsp;here&nbsp;) bob.myHeight = 60 bob.myWeight = 180 bob.myAge = 32 local sue= display:newImageRect(&nbsp;args &nbsp;here&nbsp;) sue.myHeight = 60 sue.myWeight = 180 sue.myAge = 32 bob.myFriend = sue sue.myFriend = bob&nbsp;

Now, cleanup is easy:

bob:removeSelf() sue:removeSelf() bob = nil sue = nil&nbsp;

The moral of this story is use fields on objects instead of local or global variables when you can.  Just be cautious about field names (don’t use default field names) and about referencing one object from another. i.e. If we only wanted to remove bob, we’d do this:

bob:removeSelf() sue.myFriend = nil -- Clean up reference to bob bob = nil&nbsp;

I will continue to look at your code and see if I find any more things or the root of your troubles.

OK, I don’t see anything else.  Again, don’t be sure you’ve got a leak till you really inspect it.

MY Developer’s profiler is $10 and great for finding leaks.  Also, my tool ($5) will give you a clear view of your apps behavior:

http://www.youtube.com/watch?v=XIpZ3mQsbz4

Cheers,

Ed

leak.png

I’ve used your bit of code roaminggamer, 

And this “new high” keeps popping up every 20-30 sec. It pops up instantly if the “fastkill” part of the code is triggered.

As far as the nilling and variables - 

I’ve read somewhere that used local variables for math.random is better than simply using math.random, is that true?

and i’ve nilled everything because I was just clueless as to where the leak was coming from so I tried everything I could think of 

** FIXED TYPO/VERBAGE** 1805 PST

@danlinenberg,

Looks like we’re in different time zones or you are an early riser. :slight_smile:

If the ‘new high’ keeps printing and rising, then you have probably got a leak.  Please, note that the code above was written quickly and not sophisticated.  So I can only say that it looks like you have a leak.   

So, I do suggest the M.Y. Developers profiler which has a variable (global and local) analyzer to help find leaks.  It does take some work, but it will be worth it if you find and clean up a leak.

Re: Localizing math (and other) functions

A localized function is always faster than one in the global space or attached to the namespace of a library (like math).

There are three (general) levels of localization:

  1. File Level - The local is defined in a file outside the scope of a function. - FAST ; BESTin most cases
  2. Function Level - The local is defined in a function before it is used and outside any loop bodies. - FASTEST for functions that run large (100K+) loops.  Enven then, this is only minutely faster than file level.  
  3. Loop-Level - The local is defined in a loop, or some other scope modifier (not a function definition). - SLOW ;  This is slow because every time you get back to the top of the loop, you re-initialize the local variable holding the handle to the function.  This incurs a look-up cost which is what localization practices are meant to reduce.

If I were asked to vote I would say, always use File Level Localization.  See example below:

-- Top of file local mRand = math.random local mSqrt &nbsp;= math.sqrt local newCircle = display.newCircle -- ...&nbsp; local function doit() &nbsp; &nbsp; &nbsp; &nbsp;newCirlce( args ) &nbsp; &nbsp; &nbsp; local mysqr = mSqrt( 121 ) end -- End of File&nbsp;

Parting factoid:

I am currently working on a Corona SDK performance profiler (measuring performance of Corona and Lua builds).  In my tests so far, I have found that, 

mSqrt (as shown above) vs. math.sqrt (called directly) is 26% faster for same number of ops, or alternately capable of 1.36X as many ops in same time.

3.39 million ops/s vs 2.5 million ops/sec. 

Speedup == 1 - (2.5 / 3.39) ~= 0.26 (i.e. 26%)