Flash garbage collection

Tagged:  

The garbage collection in Flash is all done behind the scenes, and it takes some tricks up the sleeve in order to ensure things get garbage collected, and also to check whether things are garbage collected.

Testing whether something has been garbage collected.
Forcing garbage collection to run.

I strongly believe in test driven development. It is much easier to make a clean application that doesn't have memory leaks if you're testing for them from step one.

So how do you tell if something is getting garbage collected or not?

When you have an object, that object will remain in memory until there are 0 references left.

package {
	public class GarbageTruck {
		private var bar:Object;
		public function foo():void {
			// bar will remain in memory until the reference "bar" is set to null.
			bar = new Object();
		}
	}
}

To check whether an object has actually been garbage collected, you have to create a weak reference to that object, then check to see if the weak reference becomes null when you've cleared all normal references to the object.

To create a weak reference, you can use a Dictionary class, set weakKeys to true, and use the object you're trying to clear as a key.

package {
	import flash.utils.Dictionary;	
 
	public class GarbageTruck {
		private var bar:Object;
		private var dict:Dictionary;
 
		public function foo():void {
			bar = new Object();
			dict = new Dictionary(true);
			dict[bar] = true;
 
			// bar will hopefully get garbage collected now.
			bar = null;
 
			gcTest();
		}
		private function gcTest():void {
			// has it?
			var cleared:Boolean = true;
			for each (var key:* in dict) {
				cleared = false;
			}
			if (cleared) {
				trace("Yes, it has.");
			} else {
				trace("No, it hasn't.");
			}
		}
	}
}

If you run this example, you will find that the variable bar will get eventually cleared, but not until some indiscriminate time later. I won't even begin to attempt to figure out Flash's rules on when certain things get garbage collected.

Forcing the garbage collector to run.

A way to force garbage collection in the flash debugger player is to call System.gc() twice. It doesn't seem to work the first time, but calling it twice in a row forces the garbage collection. Then there is also the LocalConnection hack where if you call LocalConnection.connect("foo"); twice in a row, that will also force garbage collection.

You really should only be using this in a testing environment to make sure your objects are getting cleaned up, and not in production code.

package {
	import flash.net.LocalConnection;
	import flash.system.System;
	import flash.utils.Dictionary;	
 
	public class GarbageTruck {
		private var bar:Object;
		private var dict:Dictionary;
 
		public function foo():void {
			bar = new Object();
			dict = new Dictionary(true);
			dict[bar] = true;
 
			// bar will hopefully get garbage collected now.
			bar = null;
 
			// first approach
			gcHack();
 
			// second approach, one or the other.
//			System.gc();
//			System.gc();
 
 
			gcTest();
		}
		private function gcTest():void {
			// has it?
			var cleared:Boolean = true;
			for each (var key:* in dict) {
				cleared = false;
			}
			if (cleared) {
				trace("Yes, it has.");
			} else {
				trace("No, it hasn't.");
			}
		}
		public function gcHack():void {
			// unsupported technique that seems to force garbage collection
			try {
				new LocalConnection().connect('foo');
				new LocalConnection().connect('foo');
			} catch (e:Error) {}
		}
	}
}

I was told about these techniques by some colleagues of mine, and using "the google", have found a couple similar articles:

http://www.dreaminginflash.com/2007/12/20/another-way-to-force-the-garbage-collection/
http://gskinner.com/blog/archives/2006/08/as3_resource_ma_2.html