During some recent memory profiling I was reacquainted with just how many ways there are to unknowingly allocate an object in AS3. The problem is seldom the allocation itself, but rather the later garbage collection (GC) to delete those objects. Ever used a Flash profiler only to see a huge chunk of your CPU time going to [reap], [mark], or [sweep]? Yeah, that’s the GC at work. Today’s article talks about some of the ways you end up allocating objects in AS3 without using the new keyword. These subtle errors can end up costing you!

First of all, concatenating strings is an allocation. These innocent looking lines can be performance killers:

// 2 String allocations
var fullName:String = person.first + " " + person.last;
 
// N string allocations
for (var i:int = 0; i < N; ++i)
{
    trace("person #" + i);
}

Next up are the Vector3D and Matrix3D classes. Sadly, such core built-in classes for 3D math are laden with allocations! (see Improving Vector3D for some ways around these)

// Allocates the result
v = vec1.add(vec2);
v = vec1.subtract(vec2);
v = vec1.crossProduct(vec2);
mat.transformVector(v);
 
// Allocates a Vector.<Number> with length 16
mat.rawData;

And now the dreaded Array and Vector classes. These are chock-full of methods that allocate.

// Allocates a new Array/Vector with size a.length+b.length
a.concat(b);
 
// Allocates at least one String large enough to hold
// the toString() representation of all the elements of
// the Array/Vector plus the separator characters. If
// the elements of the Array/Vector are not Strings,
// allocates a String for every element
a.join();
 
// Allocates the result Array/Vector with size a.length
a.map(mapFunc);
 
// Allocates a new Array/Vector with size N-M to hold the slice
a.slice(M, N);
 
// Usually allocates an array with size a.length
a.sort();
 
// Allocates an Array/Vector with size N to hold the removed elements
a.splice(index, N);

Some of the above can be distressing. For example, consider the totally standard “remove from a list” function:

function removePerson(name:String): void
{
    var index:int = people.indexOf(name);
    if (index >= 0)
    {
        people.splice(index, 1);
    }
}

Well, that does shrink the Array/Vector, but it also (counterintuitively) allocates an Array/Vector in the process! Consider this alternative:

function removePerson(name:String): void
{
    var index:int = people.indexOf(name);
    if (index >= 0)
    {
        var len:int = people.length - 1;
        people[index] = people[len];
        people.length = len;
    }
}

This alternative moves the last person to the location of the person to remove and then cuts off the last element of the Array/Vector. This destroys the order of the list, but oftentimes order is not important. In those cases, you can save an allocation. If order does matter, you can instead use a placeholder element:

function removePerson(name:String): void
{
    var index:int = people.indexOf(name);
    if (index >= 0)
    {
        people[index] = null;
    }
}

You will, of course, need to adjust the rest of the code using the people list to handle null elements and probably keep your own count of the list’s size since its length field will now be inaccurately high. All this trouble may be worth it though if you want to avoid allocation and preserve order.

Next, there are even some features of the language itself that result in allocation. Namely, anything that creates an activation object literally creates an activation object. You will notice in your profiler that an Object has been created on your behalf when you use these features:

function foo(): void
{
    // Allocates an "activation object"
    try
    {
    }
    catch (e:Error)
    {
    }
}
 
function goo(): void
{
    // Allocates an "activation object" and a Function
    addEventListener(Event.ENTER_FRAME, function(ev:Event): void {} );
}

Lastly, for this list at least, is the seeming impossibility of creating a “ticker” to update a simulation periodically without generating tons of garbage objects. For example, consider the humble ENTER_FRAME approach:

addEventListener(Event.ENTER_FRAME, onEnterFrame);
function onEnterFrame(ev:Event): void
{
    // The "ev" Event was allocated for us
}

What about using a Timer? It’s the same problem, except you get a TimerEvent event every time your simulation ticks. How about an addFrameScript loop using tricky compiler settings and bizarre, undocumented MovieClip functionality?

addFrameScript(0, onEnterFrame, 1, onEnterFrame);
function onEnterFrame(): void
{
}

There’s no event, but you’ll see that every time the frame is entered an Array (of what?) is allocated by the Flash Player. This bodes poorly for apps with a lot of code in FLA timelines…

In conclusion, beware of the many pitfalls of object allocation in Flash. The language is powerful and easy to use, but it’s also very easy to design code or use Adobe’s APIs in a way that allocates a ton of garbage. When you see 20%+ of your CPU time going to [reap], you’ll probably be spending some time chasing down problems like I’ve found above.

Know of more hidden object allocations? Share your experiences!