Advertisement

Understanding Garbage Collection in AS3

by
Student iconAre you a student? Get a yearly Tuts+ subscription for $45 →

Have you ever used a Flash application and noticed lag in it? Still don't know why that cool flash game runs slowly on your computer? If you want to know more about a possible cause of it, then this article is for you.

We found this awesome author thanks to FlashGameLicense.com, the place to buy and sell Flash games!

Republished Tutorial

Every few weeks, we revisit some of our reader's favorite posts from throughout the history of the site. This tutorial was first published in June of 2010.


Final Result Preview

Let's take a look at the final result we will be working towards:


Step 1: A Quick Run Through Referencing

Before we get into the real subject, you first need to know a bit about how instantiating and referencing works in AS3. If you have already read about it, I still recommend reading this small step. That way, all the knowledge will be fresh in your head and you won't have trouble reading the rest of this Quick Tip!

The creation and reference of instances in AS3 is different than most people think. The instantiation (or "creation") of something happens only when the code asks to create an object. Usually, this happens through the "new" keyword, but it is also present when you use a literal syntax or define parameters for functions, for example. Examples of this are shown below:

// Instantiation through the "new" keyword
new Object();
new Array();
new int();
new String();
new Boolean();
new Date();

// Instantiation through literal syntax
{};
[];
5
"Hello world!"
true

// Instantiation through function parameters
private function tutExample(parameter1:int, parameter2:Boolean):void

After an object is created, it will remain alone until something references it. In order to do that, you generally create a variable and pass the object's value to the variable, so that it knows which object it currently holds. However (and this is the part most people don't know), when you pass a variable's value to another variable, you aren't creating a new object. You are instead creating another link to the object that both variables now hold! See the image below for clarification:

The image assumes both Variable 1 and Variable 2 can hold the smiley (i.e. they can hold the same type). In the left side, only Variable 1 exists. However, when we create and set Variable 2 to the same value of Variable 1, we are not creating a link between Variable 1 and Variable 2 (top-right part of the image), instead we are creating a link between the Smiley and Variable 2 (bottom-right part of the image).

With this knowledge, we can jump to the Garbage Collector.


Step 2: Every City Needs a Garbage Collector

It is obvious that every application needs a certain amount of memory to run, as it needs variables to hold values and use them. What isn't clear is how the application manages the objects that aren't needed anymore. Does it recycle them? Does it delete them? Does it leave the object in the memory until the application is closed? All three options can happen, but here we will talk specifically about the second and third ones.

Imagine a situation in which an application creates a lot of objects when it is initialized, but once this period ends more than half of the objects created remain unused. What would happen if they were left in the memory? They would certainly take a lot of space in it, thus causing what people call lag, which is a noticeable slow-down in the application. Most users wouldn't like this, so we must avoid it. How can we code in order to make the application run more efficiently? The answer is in the Garbage Collector.

The Garbage Collector is a form of memory management. It aims to eliminate any object that is not used and is occupying space in the system's memory. This way the application can run with mimimum memory usage. Let's see how it works:

When your application starts to run, it asks for an amount of memory from the system which will be used by the application. The application starts then filling this memory with any information you need; every object you create goes into it. However, if the memory usage gets close to the memory requested initially, the Garbage Collector runs, seeking any object not used to empty some space in the memory. Sometimes this causes a bit of lag in the application, due to the big overhead of object searching.

In the image, you can see the memory peaks (circled in green). The peaks and the sudden drop are caused by the garbage collector, which acts when the application has reached the requested memory usage (the red line), removing all unnecessary objects.


Step 3: Starting the SWF File

Now that we know what the Garbage Collector can do for us, it's time to learn how to code in order to get all the benefits from it. First of all, we need to know how the Garbage Collector works, in a practical view. In code, objects become eligible for Garbage Collection when they become unreachable. When an object cannot be accessed, the code understands that it won't be used anymore, so it must be collected.

Actionscript 3 checks reachability through garbage collection roots. At the moment an object can't be accessed through a garbage collection root, it becomes eligible for collection. Below you see a list of the principal garbage collection roots:

  • Package-level and static variables.
  • Local variables and variables in the scope of an executing method or function.
  • Instance variables from the application's main class instance or from the display list.

In order to understand how objects are handled by the Garbage Collector, we must code and examine what is happening in the example file. I will be using FlashDevelop's AS3 project and Flex's compiler, but I'm assuming you can do it on any IDE you want, since we are not going to use specific things that exist only in FlashDevelop. I have built a simple file with a button and text structure. Since this isn't the objective in this quick tip, I will quickly explain it: when a button is clicked, a function fires. At any time we want to display some text in the screen, you call a function with the text and it is displayed. There is also another text field to show a description for buttons.

The objective of our example file is to create objects, delete them and examine what happens to them after they are deleted. We will need a way to know whether the object is alive or not, so we will add an ENTER_FRAME listener to each of the objects, and make them display some text with the time they've been alive. So let's code the first object!

I created a funny smiley image for the objects, in tribute to Michael James Williams's great Avoider game tutorial, which also uses smiley images. Each object will have a number on its head, so we can identify it. Also, I named the first object TheObject1, and the second object TheObject2, so it will be easy to distinguish. Let's go to the code:

private var _theObject1:TheObject1;

private function newObjectSimple1(e:MouseEvent):void
{
	// If there is already an object created, do nothing
	if (_theObject1)
		return;
	
	// Create the new object, set it to the position it should be in and add to the display list so we can see it was created
	_theObject1 = new TheObject1();
	_theObject1.x = 320;
	_theObject1.y = 280;
	_theObject1.addEventListener(Event.ENTER_FRAME, changeTextField1);
	
	addChild(_theObject1);
}

The second object looks almost the same. Here it is:

private var _theObject2:TheObject2;

private function newObjectSimple2(e:MouseEvent):void
{
	// If there is already an object created, do nothing
	if (_theObject2)
		return;
	
	// Create the new object, set it to the position it should be in and add to the display list so we can see it was created
	_theObject2 = new TheObject2();
	_theObject2.x = 400;
	_theObject2.y = 280;
	_theObject2.addEventListener(Event.ENTER_FRAME, changeTextField2);
	
	addChild(_theObject2);
}

In the code, newObjectSimple1() and newObjectSimple2() are functions that are fired when their corresponding button is clicked. These functions simply create an object and add it in the display screen, so we know that it was created. Additionally, it creates an ENTER_FRAME event listener in each object, which will make them display a message every second, as long as they are active. Here are the functions:

private function changeTextField1(e:Event):void
{
	// Our example is running at 30FPS, so let's add 1/30 on every frame in the count.
	_objectCount1 += 0.034;
	
	// Checks to see if _objectCount1 has passed one more second
	if(int(_objectCount1) > _secondCount1)
	{
		// Displays a text in the screen
		displayText("Object 1 is alive... " + int(_objectCount1));
		
		_secondCount1 = int(_objectCount1);
	}
}
private function changeTextField2(e:Event):void
{
	// Our example is running at 30FPS, so let's add 1/30 on every frame in the count.
	_objectCount2 += 0.034;
	
	// Checks to see if _objectCount2 has passed one more second
	if(int(_objectCount2) > _secondCount2)
	{
		// Displays a text in the screen
		displayText("Object 2 is alive... " + int(_objectCount2));
		
		_secondCount2 = int(_objectCount2);
	}
}

These functions simply display a message on the screen with the time the objects have been alive. Here is the SWF file with the current example:


Step 4: Deleting the Objects

Now that we have covered the creation of objects, let's try something: have you ever wondered what would happen if you actually delete (remove all references) an object? Does it get garbage collected? That's what we will test now. We are going to build two delete buttons, one for each object. Let's make the code for them:

private function deleteObject1(e:MouseEvent):void
{
	// Check if _theObject1 really exists before removing it from the display list
	if (_theObject1 && contains(_theObject1))
		removeChild(_theObject1);
	
	// Removing all the references to the object (this is the only reference)
	_theObject1 = null;
	
	// Displays a text in the screen
	displayText("Deleted object 1 successfully!");
}
private function deleteObject2(e:MouseEvent):void
{
	// Check if _theObject2 really exists before removing it from the display list
	if (_theObject1 && contains(_theObject2))
		removeChild(_theObject2);
	
	// Removing all the references to the object (this is the only reference)
	_theObject2 = null;
	
	// Displays a text in the screen
	displayText("Deleted object 2 successfully!");
}

Let's take a look at the SWF now. What do you think will happen?

As you can see. If you click "Create Object1" and then "Delete Object1", nothing really happens! We can tell the code runs, because the text appears in the screen, but why doesn't the object get deleted? The object is still there because it wasn't actually removed. When we cleared all references to it, we told the code to make it eligible for garbage collection, but the garbage collector never runs. Remember that the garbage collector will only run when the current memory usage gets close to the requested memory when the application started to run. It does make sense, but how are we going to test this?

I'm certainly not going to write a piece of code to fill our application with useless objects until the memory usage gets too big. Instead, we will use a function currently unsupported by Adobe, according to Grant Skinner's article, which forces the Garbage Collector to run. That way, we can trigger this simple method and see what happens when it runs. Also, from now on, I will refer to Garbage Collector as GC, for the sake of simplicity. Here's the function:

private function forceGC(e:MouseEvent):void
{
	try
	{
		new LocalConnection().connect('foo');
		new LocalConnection().connect('foo');
	}
	catch (e:*) { }
	
	// Displays a text in the screen
	displayText("----- Garbage collection triggered -----");
}

This simple function, which only creates two LocalConnection() objects, is known to force the GC to run, so we will call it when we want this to happen. I don't recommend to use this function in a serious application. If you are doing it for test, there are no real problems, but if it's for an application that will get distributed to people, this isn't a good function to use, since it may incur negative effects.

What I recommend for cases like this is that you just let the GC run at its own pace. Don't try to force it. Instead, focus on coding efficiently so that memory issues don't happen (we will cover this in Step 6). Now, let's take a look at our example SWF again, and click the "Collect Garbage" button after creating and deleting an object.

Have you tested the file? It worked! You can see that now, after deleting an object and triggering the GC, it removes the object! Notice that if you don't delete the object and call the GC, nothing will happen, since there is still a reference to that object in the code. Now, what if we try to keep two references to an object and remove one of them?


Step 5: Creating Another Reference

Now that we have proved that the GC works exactly as we wanted, let's try something else: link another reference to an object (Object1) and remove the original. First, we must create a function to link and unlink a reference to our object. Let's do it:

private function saveObject1(e:MouseEvent):void
{
	// _onSave is a Boolean to check if we should link or unlink the reference
	if (_onSave)
	{
		// If there is no object to save, do nothing
		if (!_theObject1)
		{
			// Displays a text in the screen
			displayText("There is no object 1 to save!");
			
			return;
		}
		
		// A new variable to hold another reference to Object1
		_theSavedObject = _theObject1;
		
		// Displays a text in the screen
		displayText("Saved object 1 successfully!");
		
		// On the next time this function runs, unlink it, since we just linked
		_onSave = false;
	}
	else
	{
		// Removing the reference to it
		_theSavedObject = null;
		
		// Displays a text in the screen
		displayText("Unsaved object 1 successfully!");
		
		// On the next time this function runs, link it, since we just unlinked
		_onSave = true;
	}
}

If we test our swf now, we will notice that if we create Object1, then save it, delete it and force the GC to run, nothing will happen. That is because now, even if we removed the "original" link to the object, there is still another reference to it, which keeps it from being eligible for garbage collection. This is basically all you need to know about the Garbage Collector. It isn't a mystery, after all. but how to we apply this to our current environment? How can we use this knowledge to prevent our application from running slowly? This is what Step 6 will show us: how to apply this in real examples.


Step 6: Making Your Code Efficient

Now for the best part: making your code work with the GC efficiently! This step will provide useful information that you should keep for your entire life - save it properly! First, I'd like to introduce a new way to build your objects in your application. It is a simple, but effective way to collaborate with the GC. This way introduces two simple classes, which can be expanded to others, once you understand what it does.

The idea of this way is to implement a function - called destroy() - on every object that you create, and call it every time you finish working with an object. The function contains all the code necessary to remove all references to and from the object (excluding the reference which was used to call the function), so you make sure the object leaves your application totally isolated, and is easily recognized by the GC. The reason for this is explained in the next step. Let's look at the general code for the function:

// Create this in every object you use
public function destroy():void
{
	// Remove event listeners
	// Remove anything in the display list
	// Clear the references to other objects, so it gets totally isolated
	
}

// ...
// When you want to remove the object, do this:
theObject.destroy();

// And then null the last reference to it
theObject = null;

In this function, you'll have to clear everything from the object, so it remains isolated in the application. After doing that, it will be easier for the GC to localize and remove the object. Now let's look at some of the situations in which most memory errors happen:

  • Objects that are used only in an interval of execution: be careful with these ones, as they can be the ones that consume a lot of memory. These objects exist only for some period of time (for example, to store values when a function runs) and they aren't accessed very often. Remember to remove all references to them after you're done with them, otherwise you can have many of them in your application, only taking memory space. Keep in mind that if you create a lot of references to them, you must eliminate each one through the destroy() function.
  • Objects left in the display list: always remove an object from the display list if you want to delete it. The display list is one of the garbage collection roots (remember that?) and so it is really important that you keep your objects away from it when removing them.
  • Stage, parent and root references: if you like to use a lot these properties, remember to remove them when you're done. If a lot of your objects have a reference to these, you may be in trouble!
  • Event listeners: sometimes the reference that keeps your objects from getting collected is an event listener. Remember to remove them, or use them as weak listeners, if necessary.
  • Arrays and vectors: sometimes your arrays and vectors can have other objects, leaving references within them which you may not be aware of. Be careful with arrays and vectors!

Step 7: The Island of References

Although working with the GC is great, it isn't perfect. You have to pay attention to what you are doing, otherwise bad things can happen with your application. I'd like to demonstrate a problem that may crop up if you don't follow all the required steps to make your code work with the GC properly.

Sometimes, if you don't clear all the references to and from an object, you may have this problem, especially if you link a lot of objects together in your application. Sometimes, a single reference left can be enough for this to happen: all your objects form an island of references, in which all the objects are connected to others, not allowing the GC to remove them.

When the GC runs, it performs two simple tasks to check for objects to delete. One of these tasks is counting how many references each object has. All objects with 0 references get collected at the same time. The other task is to check if there is any small bunch of objects that link to each other, but can't be accessed, thus wasting memory. Check the image:

As you can see, the green objects can't be reached, but their reference counting is 1. The GC performs the second task to check for this chunk of objects and removes all of them. However, when the chunk is too big, the GC "gives up" on checking and assumes the objects can be reached. Now imagine if you have something like that:

This is the island of references. It would take a lot of memory from the system, and wouldn't be collected by the GC because of the complexity of it. It sounds pretty bad, huh? It can be easily avoided, though. Just make sure you have cleared every reference to and from an object, and then scary things like that won't happen!


Conclusion

This is it for now. In this Quick Tip we learned that we can make our code better and more efficient in order to reduce lag and memory issues, thus making it more stable. In order to do this, we have to understand how referencing objects work in AS3, and how to benefit from them to make the GC work properly in our application. Despite the fact that we can make our application better, we have to be careful when doing it - otherwise it can get even messier and slower!

I hope you liked this simple tip. If you have any questions, drop a comment below!

Advertisement