High-Performance, Garbage-Collector-Friendly Code

| by Martin Wells

16 milliseconds is not a lot of time. Try eating a hotdog that fast – though I swear I’ve seen our dog go through a beef sausage in under 100 milliseconds.

If you want your game to run at 60 frames per second, 16 milliseconds is all you have to get everything done: moving bullets around, creating new entities, drawing sprites, checking collisions, tracking and changing states, handling input and playing sound. Whatever happens regularly in the main game cycle needs to operate as efficiently as possible. Even at a lower-performing frame rate of 30 per second, you’ll still only have 32 milliseconds to get everything done. Speed and efficiency are important, and that importance increases over time as you add more things to your game.

What’s garbage collection, and why do I care?

If you’re developing a game that has many things happening at the same time, say a weapon that fires missiles 5 times per second – not an unreasonable weapon fire rate for a high-speed shooter – you’ll quickly discover that one area of substantial impact to performance is object construction and subsequent garbage collection.

As your classes grow in size, and possibly in complexity, you’ll begin to notice lag introduced when you construct something new; this is exacerbated if you’re doing complex setup of sprites and sounds (even if they are cached in static resources). Creating lots of objects also has the side effect of generating garbage collection.

For those new to the concept, JavaScript, like many other interpreted languages, frees you from having to manage memory; you can create objects at will, without really having to keep track. The browser (or to be more specific, the JavaScript Virtual Machine running inside the browser) will come along periodically and clean up anything you’re not using anymore. This part of the system is the garbage collector (GC) – you can think of it like the ultimate housemaid.

Depending on the browser, and number of objects you have in use, the process of garbage collection can take anywhere from 10ms to 2000ms. The more things it has to check to be cleaned, and the more cleaning it has to do, the longer it will take. If you’re writing a game with independently moving objects, like an action game, this pause is going to at best noticeable, or, at worst, a series of frustrating stalls that kills the experience.

Garbage-collector-friendly code

For the most part, it’s pretty easy to write code that makes life easy for the garbage collector. The only thing you really need to do is not keep references to things you’re not using. For example, here’s some typical code:

1
2
3
4
5
6
function test()
{
   var myString = 'a string';
}

test();

After the function test has executed, the variable myString will be marked as available to be freed/deleted. This is because the function creates a scope for the variables declared within it. Upon entering the function the browser will create space for the myString variable, and once it ends, everything created in that scope will be marked for collection. At some point later (it’s up to the browser to determine when that is) the GC will run and the myString object will be “truly” removed and memory freed up.

Of course, if there are any remaining references, the GC won’t collect the variable. For example:

1
2
3
4
5
6
7
8
9
var another = null;

function test()
{
    var str = 'A string I am';
    another = str;
}
    
test();

In this case, the another variable, which is in global scope, is a reference to the string created inside the function. Since the scope is global, the garbage collector will continue to think it’s active.

Also watch out for any cases where you leave off the var keyword. JavaScript interprets this as declaring a globally scoped variable, for example:

1
2
3
4
5
6
7
8
9
// var b = null; // commented out now

function test()
{
    var str = 'A string I am';
    b = str; // oops, no var keyword means this is a global 
}
    
test();

Without the var keyword you could be assuming the b variable will be freed because the function has gone out of scope, which is not the case.

How to really delete an object

So this raises the question of how do you really make sure the stuff you create gets deleted, i.e. cleaned up by the garbage collector?

Firstly, JavaScript has a built-in delete keyword, so you can just use that right? Unfortunately, no. delete is there so you can remove properties from an object. Whilst that serves as an indirect way of clearing a reference to an object or variable (that is pointed to by the property), it’s not explicitly a way of deleting a variable. delete is useful when you want to explicitly make the property of an object undefined, rather than just null. For example:

1
2
var s = { data: 'test' };
delete s.data;

s.data will now be undefined (and also marked for removal by the garbage collector).

delete falls down though if you try to do the same with a variable:

1
2
3
var m = 'test';
delete m; // silently returns false (not allowed)
m === 'test'; // true - oops, still a value

The delete call will run just fine, but it will silently fail to delete the variable (since it’s not a property) and therefore the reference won’t be cleared and the memory won’t be reclaimed by the garbage collector. The delete call will actually return false, to indicate the variable couldn’t be deleted; how nice of it.

The right way to clear a variable is pretty simple: set every reference it to to null, then let the garbage collector do its job.

1
2
3
var m = 'test';
m = null;
m === 'test'; // false

This applies to contained properties and objects as well, so if you do:

1
2
3
var s = { data: 'test' };
s.data = null;  // not required
s = null;   // this will automatically clear s.data as well

Then the data variable is going to be automatically cleared up as well – since no reference remains to it because its outer-scoped parent object s was cleared.

Avoid creating objects

Of course, the easiest way to minimize garbage collection is to not create objects at all. The most obvious to look for is using the new keyword:

1
var newObject = new MyObject();

But there are some less obvious cases, such as:

1
2
var a = [];   // creates a new array object
var t = { };  // creates a new object

Remember that functions are treated as objects as well, so:

1
2
3
4
function getCompare()
{
    return function(a, b) { return a < b; }
}

This code will create a new (function) object on every call. Since a function is an object in JavaScript, the garbage collector has to handle those as well.

Another common source is returning compound results from a function:

1
2
3
4
function getResult(}
{
    return { result: true, value: 'test' }; // creates object every call
}

And finally, the potentially very painful Array.slice:

1
var b = a.slice(1); // creates an entirely new duplicate array

Array.slice always creating a new object/array on every call is an important one to watch out for. There’s effectively no way to clear an object out of an array without feeling this pain. Whilst modern browsers are doing lots of nice optimizations around this, it’s still a killer when done excessively. (We avoid this in the Playcraft engine by using mostly doubly-linked lists – you can find one in gamecore.js)

Measuring garbage collection

Even if you minimize how many objects you create, you’ll still be chewing through memory. To keep track of how much you’re using, and how hard the garbage collector is working to keep up with you, you can use a pretty simple technique.

Chrome will give you access to the state of the JavaScript heap (the memory allocated to JavaScript objects), but to get it to work you’ll need to add the command line switch:

1
chrome --enable-memory-info

If you want to set this up more permanently, you can create a shortcut on Windows, or on OSX create an AppleScript, such as:

1
2
3
do shell script 
  "\"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome\"     
      --enable-memory-info"

Save the script and use it as your new Chrome launcher.

Once you have Chrome running with the memory profiling enabled you’ll have access to two new properties:

1
2
window.performance.memory.totalJSHeapSize;  // currently used heap memory
window.performance.memory.usedJSHeapSize; // total heap memory

These two values represent how much memory has been allocated to JavaScript, and how much of that allocated memory is currently in use by all the variables/objects. If you regularly output the used heap size, you can keep an eye on how much memory your game is using. Normally you can just add this to your game cycle (dump the memory level every 60 seconds), but here’s an example in a wrapped timeout.

This code runs every 100ms and checks to see if the amount of memory in use on the heap (the value of usedJSHeapSize) is smaller than the last time we cycled. If we’ve suddenly used less, then we know the garbage collector has stepped in and freed up memory.

This is just a simple example. It’s pretty easy to add things like checking the memory consumption along the way, or showing a delta to see how much memory is being used over time. Using these variables I added a dynamic graph to Playcraft’s debugging panel that shows the heap data in real time. It’s blue and shiny, but that’s optional.

Object Pooling

Once you’ve eliminated as many cases of excessive object creation as you can, you’ll still be left with objects you just absolutely can’t avoid creating during that high-speed cycling of your game. Weapons fire, particle rain and explosive debris are all cases where you can be creating and destroying tens of objects per second. Have too many of these happening at the same time, and you’ll quickly start to see garbage collector pauses.

And here’s the ultimate solution to the performance issues relating to creating and destroying objects: don’t. Well, at least, minimize the number of times objects are being created and destroyed. A great technique for doing that is known as object pooling.

In simple terms, object pooling is the process of creating a pool of objects once, but instead of deleting an object when it’s finished being used (such as a bullet hitting a wall and exploding), you return it to the pool for later use. Your game then only cycles and renders the objects that are marked as active. Because the object is never dereferenced (deleted) it won’t be garbage collected, thus you can avoid both the creation of the new object, as well as the deleting.

A simple example of this is to just create a pool of objects using two arrays:

This is a fair bit of code, but the concept is pretty simple. When starting, 20 new bullets will be constructed and pushed onto the bulletPool array. When you want to fire a bullet, instead of doing:

1
var b = new Bullet();

You use the factory function:

1
var b = getNewBullet();

This will avoid construction by just returning one of the pre-constructed objects from the array, or if there are none available, fall back to constructing another bullet - essentially automatically expanding the pool.

Once you’re done with the bullet, you need to return it to the pool:

1
2
if (bullet.collidingWith(enemyShip))  // boom!
  freeBullet(bullet); // return it to the pool

One thing to remember here is the object coming back from the pool isn’t necessarily a new one, it could have just been returned to the pool. That means you need to be careful to reinitialize all of the values back to an original state. An updated factory method that handles this would be:

So that’s all cool, but in a complex game there are still some issues: first, as your game gets bigger you’re writing lots of code to support pooling on all the different types of objects. Second, without Array.indexOf (not supported in IE8) you have to sweep through the entire array in order to remove an object from the used object pool, which is slow.

What if there was a cool automagically object pooling system that implemented pooling using static inheritance, high-speed doubly-linked lists, and contained and a class system to wrap it in? Well…

Gamecore.js - Automatic Pooling

To help solve these problems, I helped write gamecore.js: a collection of high-performance JavaScript game tools. It contains a bunch of cool features, but the relevant part for this article is an automatic system for pooling any object.

To get started, you’ll need to check out a copy of gamecore.js from github:

1
git clone https://github.com/playcraft/gamecore.js.git

Which will create a gamecore.js directory.

To include gamecore.js’s object pooling you’ll need to add the following scripts to your page load (or add them to your resource manager):

1
2
3
4
5
<script type="text/JavaScript" src="gamecore.js/src/class.js"></script>
<script type="text/JavaScript" src="gamecore.js/src/gamecore.js"></script>
<script type="text/JavaScript" src="gamecore.js/src/jhashtable.js"></script>
<script type="text/JavaScript" src="gamecore.js/src/linkedlist.js"></script>
<script type="text/JavaScript" src="gamecore.js/src/pooled.js"></script>

The pooling system requires some complexities like static inheritance, so first you need to use the built-in class system. Here’s a Bullet class example:

You can then construct a new Bullet just like any other object:

1
var newBullet = Bullet(10, 10);

To convert a class to be automatically pooled you need to do two things: inherit the Bullet class from gamecore.Pooled, rather than gamecore.Base, and optionally add a create method to act as a factory. Here’s a pooled version of the Bullet class:

Notice how the class now uses a statically inherited create method, and the instance init constructor is now empty. That’s because objects are constructed by the pool automatically, without parameters. It’s only when you call the create method that the parameters are relevant. You have the option of adding construction to the init method, such as loading a sprite or configuring complex class properties, but generally it’s just setup stuff.

To then construct a new bullet, with automagical pooling, you use the create method like so:

1
2
// construct a new bullet 
var newBullet = Bullet.create(10, 10);

And, once done with it, release the object back to the pool:

1
2
3
// check to see if the bullet needs to go away
if (bullet.collidesWith(wall))
  bullet.release();

The gamecore.js pooling system will automatically take care of creating and managing the pools, as well as expanding the pool if it runs out.

Internally, gamecore.js is using high-speed doubly-linked lists to quickly move objects between the used and free pools. This works well even in IE8.

Pool Stats

Pooling is a great boost to speed up your game, however as you start to use it more in your code you’ll inevitably forget to release an object back into the pool. This will cause the pools to grow in size, and you’ll be wasting resources creating more objects than you need.

To help with this problem, gamecore.js includes two nifty features: pool stats and allocation tracing.

Pool stats are always enabled, so at anytime you can call the static class method: gamecore.Pool.getStats(), which will return a string containing the number of objects in the used and free pools for all object types. Here’s an example from a game using pooling extensively:

1
2
3
4
5
6
7
8
9
10
11
pc.components.Expiry (free: 1 used: 4)
pc.components.Attachment (free: 1 used: 4)
pc.components.Sprite (free: 0 used: 9)
pc.Sprite (free: 0 used: 9)
pc.components.Physics (free: 3 used: 34)
pc.components.Spatial (free: 1 used: 36)
pc.components.Rect (free: 4 used: 26)
pc.Color (free: 7 used: 60)
pc.Entity (free: 2 used: 35)
pc.Rect (free: 0 used: 1)
pc.Point (free: 3 used: 207)

I often bind a keyboard input so I can dump the stats at any point in a game, that way you can see where objects are being created, and usually quickly see if pools are growing beyond what they should be – which usually leads to a memory leak.

In the Playcraft engine we adapted this to show a real-time feedback graph of the state of the pooling system.

You can get stats for an individual pool by just calling getStats directly on the pool object:

1
console.log( gamecore.Pool.getPool(Fighter).getStats() );

If you do find cases where objects are not being released back into the pool, the next step is to try to figure out where they are being allocated in your code. Gamecore.js has a neat tracing feature that, when enabled, will track where in your code objects are being acquired, including the count. This makes is much easier to see where large numbers of objects are being acquired but not released.

Tracing is slow though, so it should only be used on a limited number of object pools, and explicitly when you’re trying to track down an object leak.

You can enable tracing on any pool, for example:

1
gamecore.Pool.getPool(Fighter).startTracing();

And then stop tracing using:

1
gamecore.Pool.getPool(Fighter).stopTracing();

You can dump the results of a trace using the same getStats function call above:

1
console.log( gamecore.Pool.getPool(Fighter).getStats() );

Except with tracing enabled you’ll get a list of where in your code the allocations are taking place:

1
2
Class.Fighter (http://localhost:2020/game/fighter.js:320:46) (60)
Class.Enemy (http://localhost:2020/game/enemy.js:142:12) (764)

This shows the Fighter pool has had objects allocated from the fighter.js and enemy.js source files. The two numbers following the source URL is the line and column number in the file, and the final number in brackets is the number of objects allocated by that code. From this output you can see that it’s likely the enemy.js allocations are not being released.

Conclusion

Part of the reason JavaScript is such a fun, easy and fast language is due to its handling of most memory issues for you. You can just code away and let it take care of the clean up.

However, that helpful automatic garbage collector can sometimes step in at the wrong time and cause issues with your game. But with some care it’s easy to minimize the number of objects you create, as well as make sure they are ready for collection.

When that isn’t enough, techniques like object pooling help substantially reduce garbage collection by removing it from the picture entirely.

At Playcraft we’ll be continuing to improve the object pooling system, and your suggestions and code contributions are most welcome.

About the Author

Martin Wells is the CEO and Founder of Playcraft Labs. An Australian native now living in San Francisco, his passion and focus have been on games since the age of 15. He is the Author of J2ME Game Programming—an award-winning 800 page book on advanced mobile software development (published by Thomson Course). Wells was also the CEO and Founder of Tasman Studios in Sydney, Australia which produced the games Club Soccer, Hypergate and Cybots among others.

in JavaScript .

Comments