Code Optimization and Memory Leak Prevention
This article covers some of my basic practices in code optimization and memory leak prevention while programming in Lua using the Corona SDK.
Following this advice won’t provide a fail-proof way of preventing memory leaks and performance issues, but it’ll definitely help minimize them.
Prevention is Key
Rather than hacking away at all your code and then going back to check for memory leaks later once you discover something’s not right, it’s best to take as many measures as possible before ever getting to that point. That way you don’t find yourself in a “needle in a haystack” situation when your game/app is crashing for unknown reasons.
Object Reference File
For every project, I usually create separate text files for each module, where I’ll keep track of all the display objects as I create them. This helps me out a lot when it’s time to free things from memory and prevents me from being unsure if I’ve somehow missed something.
So for example, whenever I call a
display.newImage() (or something similar), I simply write the object’s name in the text file as a new line. That takes me to my next point.
Create a removeObjects() Function
Somewhere towards the bottom of your module, I find it useful to create a
removeObjects() function where I manually call
myObject = nil for every single display object that has been created. If that object has any event listeners attached to it, I also remove those listeners before calling
After I manually write all of the removal code for each object, I then loop through the display groups and remove everything as described in the Corona documentation. Once all this is complete, I have a nice function handy for whenever I need to completely remove everything from that particular module—and I can feel good about it.
Common scenario: unloading a screen completely to move onto a different one.
But all that seems a little like overkill right? Since, after all, looping through the display groups and calling
child = nil should get the job done, right?
Perhaps, but I find that putting in the extra work is more efficient and helps ensure that listeners are stopped, and that objects really ARE removed from memory so your app runs as smooth as possible. On top of that, I hate feeling insecure about things, and the extra measures makes me feel a lot more secure about my code.
Here’s a tip: don’t wait until you’re done with everything before going through your reference text file to create the removeObjects() function. Instead, when you create your display object, go ahead and add the “removal” code to the removeObjects() function as you go—that’ll decrease the overall workload.
Be Mindful of Event Listeners
A common mistake people make is ONLY removing display objects when unloading modules and/or screens in their app; don’t forget about your event listeners!
For example, if you have a main enterFrame listener, or an accelerometer listener running, those functions will continue to eat up memory and cpu until you remove them. They can also cause critical errors and crashes if the listener functions require objects that you have already removed.
The best thing to do is treat your event listeners similar to how you would with individual display objects and call
Runtime:removeEventListener() in either your
removeObjects() function or a similar one that specifically deals with your listeners.
Don’t Forget to Unload Globals
It’s recommended you make as many variables “local” as possible, but in some cases, you might need to create non-local variables—for example, when you need to access a variable from another module.
As long as it’s kept to the bare minimum, that’s fine, but don’t forget that Lua won’t garbage collect (aka “clean up unused resources”) globally scoped variables unless you set them to “nil”. So when you’re finished with a global variable, don’t forget to do this:
myGlobal = nil
If you’re using globals strictly for accessing data from other modules, it’s best to store that data as a local variable to speed up your calls to that data when you can.
For example, say you have main.lua and screen1.lua. There’s a variable, myGlobal within main.lua that you need screen1.lua to have access to. So rather than always calling myGlobal from within screen1, you can create a local variable that copies the existing data from myGlobal like so:
local myGlobalCopy = myGlobal
That way you’re only using the global variable once within screen1.lua and the rest of the time using a local variable. This is significant because in Lua, using local variables is much faster and takes up less memory than using globals.
Once again, those tips aren’t going to prevent memory leaks and optimization issues 100% of the time, but they do serve as general “best practices” to consider when coding your apps.
They will—at the very least—minimize potential memory and performance issues in your apps, so that alone should make it all worth it.