Showing all entries for: May 2011

Corona SDK Memory Management 101

Memory management has got to be the most “grey” area when it comes to creating an app using the Corona SDK. Why? Because everyone recognizes the need for it. Everyone knows it’s important, but not everyone knows exactly how to implement it, and there’s always that nagging question in the back of your head asking, is my app leaking memory?. First though…

What is Memory Management?

This is not something that’s new to Corona projects. When it comes to programming any kind of app, no matter what framework you’re using, no matter what language, to some extent you have to manage memory.

Thankfully, with Lua and Corona, you only have to worry about the logical aspect of memory management, not the technical—Lua handles that for you automatically (via garbage collecting—more on that in a moment).

Memory management in Corona is simply managing the following five things:

  1. Display objects
  2. Global variables
  3. Runtime listeners
  4. Timers
  5. Transitions

If you fail to manage any of the five things above, you can run into a nasty thing called a memory leak. But is there a way to track memory leaks? There’s various ways to go about doing it, but the most simple way is to place the following code at the bottom of your main.lua file (or at the bottom of every one of your screens, whichever you prefer):

local monitorMem = function()

    collectgarbage()
    print( "\nMemUsage: " .. collectgarbage("count") )

    local textMem = system.getInfo( "textureMemoryUsed" ) / 1000000
    print( "TexMem:   " .. textMem )
end

Runtime:addEventListener( "enterFrame", monitorMem )

What the above code will do is continuously print two lines to your terminal window:

MemUsage: ##
TextMem: ##

The first line represents Lua memory, and the other represents texture memory (from graphics). The amount isn’t really what’s important. What is important is that you monitor and make sure that as you go through your app, the numbers are rising and falling properly.

For example, if you go to a screen, depending on what’s on that screen the MemUsage could go up or down. Let’s say it goes up. Then, you go back to the previous screen and memory just went up again! That represents a memory leak because memory usage should have gone back down to where it was when you were previously on that screen. At some point, something got stuck in memory.

So keep that small function in mind as we go through, because it’s a great little tool for catching memory leaks. In fact, using that same function above, I’ve been able to get buggy, crashy apps to be nearly crash free (nothing is 100% crash-free, so in other words, I wasn’t able to crash it again).

You may have noticed a call to collectgarbage() above. That’s a built-in Lua function that manually invokes something that’s known as the “garbage collector”. Basically, it runs through and frees nil’ed out references to variables from memory (e.g. “garbage”). Lua does this for you automatically, but you can force it to run whenever you want by calling collectgarbage( “collect” ).

Display Objects

This is the easiest to manage, because you are directly responsible for creating display objects. The way you can prevent display object-related memory leaks from occurring is to make sure you remove and nil-out every display object that you no longer need.

Example:

display.remove( redBall )
redBall = nil

Notice how I used display.remove() instead of removeSelf()? FYI, the two functions are identical, except display.remove first checks to make sure the object isn’t already nil before calling removeSelf. It’s just a safer way to remove objects in Corona.

Where leaks occur most often in this area is when objects are created within a loop, or somewhere it’s difficult to keep track of each individual object. Just make sure you’re keeping track of everything, and that at some point, display objects are freed from memory when no longer needed—especially when changing screens.

Global Variables

A global variable is any variable you declare without putting the word local in front of it. It’s recommended that you stay away from global variables as much as you can, but if you must use them, ensure that when you’re finished with those variables, you set their value to nil at some point, so the Lua garbage collector can free it from memory.

If you’re working within the same module and you just can’t figure out how to make a variable (or function) local and still usable, then you might need to learn about forward referencing—it’s a true Lua gem.

Runtime Listeners

When you remove a display object, listeners that are attached to it are also freed from memory. However, when you add listeners to Runtime (e.g. enterFrame listeners), those never get freed until you manually remove them.

A common leak that occurs with Runtime/enterFrame listeners is when a developer adds a Runtime listener to the particular screen they’re in, but they forget to remove the event listener whenever the user leaves the screen. So what happens is, the user leaves the screen, and then when they come back to it, there are two identical Runtime listeners running on top of one another.

Sometimes, this just produces an outright crash. Other times, it bogs down your app until it eventually crashes. Neither outcome is desired.

If you find it hard to keep track of event listeners, you might find value in an article I wrote about managing Runtime listeners.

Timers and Transitions

Timers and Transitions are probably one of the most common causes of crashes, mostly due to the fact that countless (unaccounted for) things could happen between the time the timer/transition starts, and the time it ends. What’s worse, if you forget to cancel these and nil out their variables when no longer needed, things could get stuck in memory and cause more leaks.

One way you could manage these is to store ALL of your timers and transitions in a table, so that when you know for sure no timers or transitions should be running, you can cancel all of them at once.

If you add the following code to your main.lua file (or another module and require it in), you can easily keep track of your timers/transitions and also cancel them all at once if and when needed:

timerStash = {}
transitionStash = {}

function cancelAllTimers()
    local k, v

    for k,v in pairs(timerStash) do
        timer.cancel( v )
        v = nil; k = nil
    end

    timerStash = nil
    timerStash = {}
end

--

function cancelAllTransitions()
    local k, v

    for k,v in pairs(transitionStash) do
        transition.cancel( v )
        v = nil; k = nil
    end

    transitionStash = nil
    transitionStash = {}
end

Then, whenever you start a new timer or transition, you assign it to the respective table first, like so:

timerStash.newTimer = timer.performWithDelay( ...

transitionStash.newTransition = transition.to( myObject { ...

Then you can use the cancelAllTimers() and cancelAllTransitions() functions to stop all timers and transitions all at once. If you have timers you don’t want to be cancelled, then don’t add them to the “stash” tables in the first place (but be sure not to forget to keep track of them).

That’s just one way you could do it—there are literally countless solutions. You could always just manage them all manually and be just fine also.

Conclusion

Notice how I didn’t mention anything about pointers or any of that other stuff that normally goes along with memory management? Just simple, practical advice that—if applied consistently—will help you to produce stable, crash-less apps with the Corona SDK every single time.

Good luck.

Tilt Monster (aka “Doodle Dash!”) Source Code. The code has been cleaned up, bugs have been fixed, and the project (Title Monster) has been completely converted into sample code that you can learn from and/or use as a framework for your own projects.

Why Am I Doing This? Because I’ve received tons of great feedback on Ghosts vs. Monsters, and I get lots of emails from people telling me how much they’ve learned from it. Therefore, I know there’s a lot of value in nearly-complete and—in this case—complete game source code.

I’m happy to help Corona developers learn from my previous efforts, and I’m equally excited to see what kind of games pop up using Tilt Monster as the framework (the Ghosts vs. Monsters-based games I’ve seen have been awesome!).

Plus, with my family, work, and current projects in my way, I honestly think the community can give Tilt Monster more love than I can via GitHub … so here’s to Doodle Dash… err… I mean, Tilt Monster! I look forward to seeing your contributions to the source code.

Enjoy :–)

Anyway, no more questions, just download it already!

Make More Money with the Corona SDK. Let’s face it, you may love developing apps, but would your wife let you do it if you didn’t have the hope of making money from it? No answer necessary (heheh).

Well, Ansca’s got your back. The latest Daily Build (subscribers only!) allows you to easily implement the Super Rewards 3rd Party API into your apps.

Now you can add in-app purchases AND offer-based virtual currency to your apps, to open up even more income streams. I’ve personally tested the feature out and it’s extremely easy to implement (kudos to the engineering team for making all these 3rd party API’s a no-brainer to implement). The Super Rewards documentation page will walk you through setting it all up (with screenshots).

Flash Code vs. Corona SDK Code. Now THIS is awesome.