Friday, January 11, 2008

Garbage collection in AS3, flash player 9


I've been playing around with AS3 for a while now, and am really excited by its capabilities. The raw execution speed by itself will create so many possibilities. Toss in E4X, Sockets, ByteArrays, new display list model, RegEx, a formalized event and error model, and a few dozen other features for flavour, and you have a pretty heady brew.
With great power comes great responsibility, and this will be very true for AS3. A side effect of all this new control is that the Garbage Collector is no longer able to make as many assumptions about what it should tidy up automatically for you. This means that Flash developers moving to AS3 will need to develop a very strong understanding of how the GC operates, and how to work with it effectively. Building even seemingly simple games or applications without this knowledge can easily result in SWFs that can leak like a sieve, hogging all of a system's resources (CPU/RAM), and causing the user's system to hang (potentially even forcing them to hard reboot their computer).
Articles in this series
Part 1: The FP9 Garbage Collector
Part 2: Resource Management Issues in FP9
Part 3:New Tools in AS3
Soon: Strategies and Solutions Related articles
Understanding the Delete Keyword
Weakly Referenced Event Listeners
Over the next few weeks (or even months), I will be writing a series of articles on this topic. I will look at the underlying mechanics of the Garbage Collector, discuss the issues you are likely to face, examine the new tools available to you for handling resource management in AS3, and offer solutions/code to help you circumvent many of the common problems you will face.
I'll begin at the beginning, and look at the Garbage Collector in the Flash 9 player.
About the Garbage CollectorThe garbage collector is a behind-the-scenes process that is responsible for deallocating the memory used objects that are no longer in use by the application. An inactive object is one that no longer has any references to it from other active objects. In order to understand this, it is very important to realize that when working with non-primitive types (anything other than Boolean, String, Number, uint, int), you are always passing around a reference to the object, not the object itself - deleting a variable removes the reference, not the object. This is easily demonstrated: // create a new object, and put a reference to it in a:
var a:Object = {foo:"bar"}
// copy the reference to the object into b:
var b:Object = a;
// delete the reference to the object in a:
delete(a);
// check to see that the object is still referenced by b:
trace(b.foo); // traces "bar", so the object still exists.If I were to delete "b" as well in the example above, it would leave my object with no active references and free it for garbage collection. The AS3 GC uses two methods for locating objects with no active references: Reference counting and mark sweeping.
Reference CountingReference counting is one of the simplest methods for keeping track of active references, and has been around in Flash since AS1. When you create a reference to an object its reference count is incremented. When you delete a reference, its reference count is decremented. If the reference count of an object reaches zero, it is marked for deletion by the GC. For example: var a:Object = {foo:"bar"}
// the object now has a reference count of 1 (a)
var b:Object = a;
// now it has a reference count of 2 (a & b)
delete(a);
// back to 1 (b)
delete(b);
// down to 0, the object can now be deallocated by the GCReference counting is simple, doesn't carry a huge CPU overhead, and works well in most situations. Unfortunately it really falls down when it comes to circular referencing. This is when objects cross-reference each other (directly, or indirectly via other objects). Even if the application is no longer actively using the objects, their reference counts remain above zero, so they are never removed. Here's a quick demo: var a:Object = {}
// create a second object, and reference the first object:
var b:Object = {foo:a};
// make the first object reference the second as well:
a.foo = b;
// delete both active application references:
delete(a);
delete(b);In the above example, both of my active application references have been deleted. I no longer have any way of accessing the two objects from my application, but their reference counts are both 1 because they reference each other. This can also be much more complex (a references c which references b which references a, etc), and is hard to deal with in code. Flash player 6 and 7 suffered from problems related to circular referencing in XML objects - each XML node referenced both its children and its parent, so they were never deallocated. Fortunately, player 8 added a new GC technique called mark sweeping.
Mark SweepingThe second strategy employed by the AS3 (and fp8) GC to find inactive objects is mark sweeping. The player starts at the root node of your application (which is conveniently the "root" in AS3), and walks through every reference on it, marking each object it finds. It then iterates through each of the marked objects, marking their children. It continues this recursively until it has traversed the entire object tree of your application, marking everything it finds. At the end of this process, it can safely assume that any objects in memory that are not marked no longer have any active references to them, and can be safely deallocated. You can see how this works in the diagram below (green references were followed during mark sweeping, green objects are marked, white objects will be deallocated).

Mark sweeping is very accurate, but because it has to traverse your entire object structure, it is also costly in terms of CPU usage. Flash player 9 reduces this cost by carrying out iterative mark sweeping (ie. it occurs over a number of frames, instead of all at once), and by only having it run occasionally.
Deferred GC and IndeterminacyA *very* important thing to understand about the Garbage Collector in FP9 is that it's operations are deferred. Your objects will not be removed immediately when all active references are deleted, instead they will be removed at some indeterminate time in the future (from a developer standpoint). The GC uses a set of heuristics that look at RAM allocation and the size of the memory stack (among other things) to determine when to run. As a developer, you must accept that fact that you will have no way of knowing when (or even if) your inactive objects will get deallocated. You must also be aware that inactive objects will continue to execute indefinitely (until the GC deallocates it), so code will keep running (ex. enterFrames), sounds will keep playing, loads will keep happening, events will keep firing, etc.
It's very important to remember that you have no control over when your objects will be deallocated, so you must make them as inert as possible when you are finished with them. Strategies to manage this will be the focus for a future article.

0 Comments:

Post a Comment

<< Home