Garbage Collection and Flex Event Listeners

On a recent AIR project I have become aware and subsequently obsessed with memory. My concerns have centered around garbage collection and event listeners, specifically how you clean up event listeners to allow Garbage Collection (GC) to clean up objects. Most DisplayObjects implement the EventDispatcher class that allows it to broadcast events. Trolling around the googleverse I found some interesting ways to insure that you break strong references to allow clean up or manually remove event listeners on your own to make your application more memory savvy.

When you use addEventListener() method and register a event listener to an object you create reference. By default any object that has a reference to a listener will keep that reference until it is removed using the removeEventListener() method. Strong references will not be cleaned up by Flash player GC and remain in memory until the application is closed or the world ends, whichever comes first. This makes for a horribly leaky AIR applications, especially those applications that may run for days on the user’s system without being restarted.

Use Additional Parameters of Listener

The addEventListener() method does have some additional (and seldom used) parameters to create a weak reference when adding the listener to an object. On of those parameters is “useWeakReference”, the 5th parameter of the method. To utilize this parameter you need to set it from false to true:

addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false);
.....

var button:Button = new Button()
button.addEventListener(MouseEvent.click, handleMouseClick, false, 0, true);

private function handleMouseClick(event:MouseEvent):void
{
 // handle the event
}

This will allow the event to be cleaned up at some point by Flash garbage collection. If you are like me and and find it annoying that the useWeakReference parameter of the addEventListener() method is false by default, you can can extend the object and make useWeakReference true by default, then use that object in your project:

package com.custom.controls
{
	import mx.controls.Button;

	public class CustomButton extends Button
	{
		public function CustomButton()
		{
			super();
		}

		override public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=true):void
		{
			super.addEventListener(type, listener, useCapture, priority, useWeakReference);
		}

	}
}

Explicitly Removing Listeners

Even though setting useWeakReference=true will most likely mark an object for clean up, to be even more certain you should explicitly remove the event listener with the removeEventListener() method:

private var button:Button;

private function init()
{
     var button:Button = new Button();
     button.addEventListener(MouseEvent.click, handleMouseClick, false, 0, <strong>true</strong>);
}

private function handleMouseClick(event:MouseEvent):void
{
   button.removeEventListener(handleMouseClick);
   button = new Button();
   // handle the event
}

Now you are certain that you have cleaned up and removed the listener manually and the object will be removed at some point during GC since there is no longer an explicit reference to the object.

Anonymous Functions

Another technique that comes in handy is to removing event listeners when using anonymous functions as event handlers. The trick to this techniques is the arguments.callee:

var button:Button = new Button()
button.addEventListner(MouseEvent.CLICK, function(event:MouseEvent):void {
      event.currentTarget.removeEventListener(event.type, arguments.callee);
});
 

Bummer

Now for the disappointment. If you create an event listener using MXML they are always going to have strong reference by default with no way to change useWeakReference and they make it difficult to use removeEventListener() method:

private function handleClickEvent(event:Event):void
{
     // handle event
}

<mx:Button click="handleClickEvent(event)"/>

So these objects will not be cleaned up when created in this way unless you have some clean up method that explicitly removes the event listeners or you add the event listeners to MXML object using ActionScript instead of binding.

So basically you need to add objects or listeners in ActionScript rather than use MXML when using the addEventListener() method, which sort of makes MXML less valuable in some respects because binding to objects and listening to events is what drives the framework.

Additional Links

For a great discussion on the topic, see Ted Patricks post and be sure to read all the comments as many of the examples were taken from discussion.

Garbage Collection Articles:

http://spreadingfunkyness.com/garbage-collection-with-flex-and-adobe-air/
http://blogs.adobe.com/aharui/2007/03/garbage_collection_and_memory.html
http://gskinner.com/blog/archives/2006/08/as3_resource_ma_2.html
http://www.adobe.com/devnet/flashplayer/articles/resource_management.html

5 Comments

  1. Great article!

    There has been a lot of offence to Flash Platform lately, from HTML5 fanboys, Apple etc. and what the Flash community has failed to understand is that it’s actually to our benefit. Any criticism – if taken constructively – is beneficial and should be addressed. And without a doubt garbage collection is one of the things that receives insignificant attention from the community. Too many bloated and crashing (esp. on Mac/Safari) (web) apps out there.

  2. Why add an event listener to every button etc. when you can add just one listener to their parent, and use the target property of the event to find out which object was clicked? This should make stuff easier and conserve memory..

    1. This example is simplistic, but I am sure there are situations where you do want to add a listener to a button rather than the parent. Don’t think of this example as covering every use case, just a general overview.

Comments are closed.