Even though programming in ActionScript 3 in the Flash engine gives you access to a garbage collected language it is still easy to leak resources and kill the performance of your application. I’ve been looking into some issues with an application I’m working on as well as general debugging techniques and wanted to summarize them in a post.
First, the two main resource leaks that have come up for me in game development:
- Leaking objects in the display list.
- Leaking event listeners, especially ENTER_FRAME.
Leaking display list objects
This is a straightforward leak that can happen to any DisplayObject you add to a DisplayObjectContainer. Even if the object is off screen, alpha is zero, or if its visible property is set to false the object is still being updated by the Flash runtime. It is using memory and CPU cycles. Make sure you clean up your objects by removing them from the display list when they are not in use.
Here’s a small, simple code snippet for recursively walking a display list. If you suspect a DisplayObjectContainer is leaking objects, you can use this small utility function to count how many display objects exist in all children. If it doesn’t go down at all, you may be leaking objects (of course it depends on the use case of your software).
public function walkList(obj:DisplayObjectContainer):Number
{
var result:Number = 0;
if (obj == null || obj.numChildren == 0)
{
return 0;
}
result += obj.numChildren;
for (var i:int = 0; i < obj.numChildren; i++)
{
result += walkList(obj.getChildAt(i) as DisplayObjectContainer);
}
return result;
}
Leaking event listeners
This can be harder to track down than leaking display objects. Here’s the big thing that may not seem obvious to people at first – removing an object from the display list, even such that there are no more references to it, does not automatically destroy it or any of its event listeners. If you add a listener through addEventListener() you must remove it through removeEventListener().
By far the worst case of this are rogue ENTER_FRAME events that are still executing on objects you think are removed and gone.
I spent some time trying to figure out how best to track these issues down. The AS3 event model allows you to, for most events, insert an event listener in a parent object in the display hierarchy and basically monitor events as they go by. For example, if one of your child objects has a MOUSE_DOUBLECLICK event you can put the same event on your stage with the third parameter to addEventListener() set to true (capture). You can then print out what object the event is actually going to by using Event.currentTarget inside of the stage event listener.
Guess what though? You can’t capture ENTER_FRAME events in that way. You’ll have to recursively walk the display list again looking for objects that have the ENTER_FRAME event registered. If they have no parent reference then they are likely orphaned (again, depends on your software use case).
Or perhaps better yet – don’t use ENTER_FRAME at all for complex Flash software and write a traditional game update loop. That’s what I’ve done in my engine. I now only use ENTER_FRAME for hacking together prototypes.
As an addendum, here are some gotchas regarding AS3 that I learned about in my recent research:
Apparently creating static utility functions, especially math helpers used in tight loops, is significantly worse performance wise than pasting the code into the loop (aka ghetto inlining) even if complex constants would have been precomputed in the helper. Lesson: static lookup is slow.
If you can avoid it, do not call functions on class references that are typed by one of their parent classes. E.g. if you have a variable of type MyObject, try not to reference it through the global root class Object or any other parent class that may exist. This defeats the purpose of object oriented programming in many ways but for your most performance sensitive code the virtual method table lookup destroys performance.