I will be dealing with this in the next installment of my resource management series of articles, but I thought it was important enough to warrant a quick post in the interim.
I've been blogging about how important resource management is going to be in ActionScript 3, and also mentioned that we have some new tools to deal with it. One of these new tools is the ability to have weakly referenced event listeners. A weak reference is one that is not counted by the Garbage Collector (ie. it is not counted in reference counting, and it is not followed for mark sweeping). This means that if the only references remaining to an object are weak, it will be available for collection on the next GC sweep.
References associated with event listeners are often forgotten by developers, which normally results in the listener never being removed from memory. This is why weakly referenced event listeners are so handy in AS3 - if you forget to remove the listener, you will not impede the Garbage Collector's ability to collect the object.
It's easy to use weakly referenced listeners in AS3. Just set the fifth parameter of an addEventListener call to true:
// params: eventName, listener, capturePhase, priority, useWeakReference someObj.addEventListener("eventName",myFunct,false,0,true);I would very strongly recommend getting in the habit of ALWAYS setting your listeners to be weakly referenced. I can't think of any good reasons to set it to false, so I'm kind of disappointed that it wasn't set to use weak references by default, although I understand the rationale. The one place weakly referenced listeners fall apart is in the case of anonymous functions:
addEventListener("eventName",function(evt) { ...code... },false,0,true);
In the above example, the only reference to the anonymous function I defined in-line is the weak reference from the event dispatcher. This means the next time the GC sweeps, my function will be removed, and obviously will no longer be called. This can be very confusing to debug, because the indeterminate GC will make the results seemingly random.
Using anonymous functions in this way is bad practice though (both architecturally, and because it leaves you with no way to remove the listener), so I'd still argue for weak references by default. This is a little selfish though, because I know that we are going to have problems in the future with third party content creating strongly referenced stage listeners (and thus never being garbage collected), whereas my team will never encounter the anonymous function issue (unless it's in debugging third party content).
So please (PLEASE!), get in the habit of always setting your event listeners to be weakly referenced. It's a little bit of extra typing, but it'll save everyone (you, your users, anyone that needs to integrate with / consume your content) a lot of headaches.
Kudos to Adobe for providing this functionality!
Comments (18)
Great post, Grant!
I'm having doubts about adding eventlisteners inline with mxml, now. The generated as doesn't use week references either even with anonymous functions. And in this case, the compiler generates a class member to invoke instead of using the anonymous function.
I understand that it was a late arrival to the api, so maybe Adobe refine it more for 2.1?
Posted by: James Lyon at July 7, 2006 11:40 AMURL:
James,
I've done some investigation on the Flex framework, and there definitely seems to be room for improvement from a memory management standpoint. The framework really doesn't do much (any?) active memory management, and it probably should. It's less of an issue in Flex than with Flash for the reasons stated in my last post, but I'd love to see some improvements to it.
Cheers.
Posted by: Grant Skinner at July 7, 2006 12:02 PMURL: http://gskinner.com/
I've download your tests file and just run it with setting weak reference to true and... events are always dispatched. I don't understand why ?
I've also tryed to evaluate GC work by tracing FP memory all along execution. I haven't notice any action of the GC while creating objects in an enterFrame handler. I don't see any change in the memory except a small increase every 5s.
For information I trying to build a pseudo "Box Model" in order to create UI components. Then that side of conception is really a big part in the project and, as you notice in your lasts posts, the new graphic structure offer a great flexibility but also great responsabilities in ressources management, especially when you have to deal with large OO structure, where objets are frequently duplicated (to provide a css like skin management with inheritance and also an object level access).
Posted by: Cédric Néhémie at July 7, 2006 04:44 PMURL: http://book.abe.free.fr
So the Flash Player truely supports week refs. The Dictionary class can also hold items by week ref. But is it possible to create week refs to objects manually like we do in C#?
Posted by: Arrix at July 7, 2006 09:20 PMURL: http://anotherblog.spaces.msn.com
Arrix,
Not natively, no. In my next article I will be looking at ways to circumvent this, including source code.
Posted by: Grant Skinner at July 7, 2006 10:10 PMURL: http://gskinner.com/
Hi Grant,
Thanks so much for the series on resource management. This is a really important topic that is all too easy for developers to ignore. Just one thing... I have never heard mark-and-sweep called mark sweeping... seems like a malapropism. Thanks for your awesome articles!
Posted by: anonymous at July 7, 2006 10:20 PMURL:
You could be right... mark-sweep is a common term, stretching back to at least the early nineties, but I apparently decided to turn it into a verb. I'll go back and revise my terminology tomorrow. :)
Posted by: Grant Skinner at July 8, 2006 02:05 AMURL: http://gskinner.com/
Hello Grant!
Posted by: Adu tt at February 19, 2007 07:11 AMI'm developing a management aplication...The whole application is based on popups and I've noticed that when I open a popup a certain memory is being used..I close the popup and guess what...the memory is not given back...
I quess that the solution would be to set a weak reference. I've tried, but i didn't manage to do that...please help!!!!!!!!!!!!!
the example could be found here: http://adidre.go.ro/popup/erw.swf
URL: http://adidre.go.ro/popup/erw.swf
Hi Grant,
To clarify something. My understanding of automatically bound methods is that they are a bit like anonymous functions, and so if you have a class Bar with a method Bar.foo which handles events:
// from within Bar
someObj.addEventListener("eventName",foo,false,0,true);
the bound method foo will be garbage collected if you don't save a reference to it in your class like this:
this.listener = methodFoo; // save for later
someObj.addEventListener("eventName",listener,false,0,true);
Is this correct?
Posted by: Bernard Sumption at May 10, 2007 03:26 AMURL: http://berniecode.com
Oops, 'methodFoo' should be just 'foo' in the above obviously.
Posted by: Bernard Sumption at May 10, 2007 03:31 AMURL: http://berniecode.com
Hi Grant, thanks for these articles although I don't understand the rationale for not making weak references default to true. If they fall apart on anonymous functions, simply set weak references to false when using anonymous functions. Or am i missing something more?
We need as much rails methodology in the design of APIs as possible to increase fun and productivity in development. (i.e. no more attachMovie is a godsend!)
Posted by: Peter O'Brien at June 30, 2007 06:41 AMURL:
hello, I've been tracking this memory issue and found your articles, very interesting indeed I didn't knew about the weak references.
I want to add an example of meaningful annonymous functions as event listeners, they can be used to allow the listener function to receive more parameters, they act as a wrapper that calls the real listener function with the rest of the data because inline functions inherit it's environment so we can do something like this (Flex example):
import flash.events.MouseEvent;
import mx.controls.Alert;
public function init():void {
var myNumber:Number = 3;
myButton.addEventListener('click', function(event:MouseEvent):void { clickListener(event, myNumber); });
}
public function clickListener(event:MouseEvent, num:Number):void {
Alert.show(num.toString());
}
]]>
Posted by: Samus_ at November 7, 2007 06:53 PMURL: http://web2samus.awardspace.com/
Do Loader objects with event listeners attached (eg Event.COMPLETE) persist as long as the load operation is underway? I have code that initializes a Loader within a function with the onComplete listener function nested within that function and it has never failed, but some advise that this practice is risky. I seem to remember AS2's load functions working the same way as my AS3 example above seems to be working-- that they do persist until the loader's listener event is executed.
Posted by: dster at December 2, 2007 08:06 AMURL:
Hi Grant!
Thanks for the these articles on resource management. I just wanna clarify the situation below.
// in some class member
var foo:Function = function(e:Event):void
{
trace("foo called");
}
var urlLoader:URLLoader = new URLLoader();
urlLoader.addEventListener(Event.COMPLETE, foo, false, 0, true);
urlLoader.load(//something);
I tried this and some case the complete was never called. So is this same as the anonymous function?
Posted by: gen at February 5, 2008 02:23 AMI know this might not be a best practice.
URL:
In my experience, setting the week reference to true is not really helping, because the instance of my object is garbage collected at an unknown time in future. Could be in a second or few minutes. Until then my object in memory will still be listening to events, which i dont want to happen. I have to unregister the listeners explicitely before setting the object referenct to null.
Posted by: Sri at April 9, 2008 06:59 AMThere is no easy way to do this either, i have know to what events a particular object is listening to, in order to unregister. I cannot get all the listeners registered by an object, there is no API to do that.
URL:
Somehow, setting the weak references to "true" actually causes more bug than it fixes for me. I've already gotten into the habit of removing the eventListener when I don't need them. In the case of an EventListener that is regularly added and removed, setting the weak references to "true" randomly prevents the EventListener to being added back at a later time. I don't understand why, but with with weak references to false (default), this does not happen. What is happening?
Posted by: Kevin at June 24, 2008 02:38 PMURL: http://gamesandmen.blogspot.com
Hi Grant,
Posted by: Sorv at July 16, 2008 12:29 AMI can understand the rationale for this, but doesn't saying "It's also an example of bad practice because it doesn't use weak references for the listener" (one of your comments on http://www.gskinner.com/blog/archives/2008/07/additional_info.html) take it a little bit to far? I mean we have tons of references, which all needs to be cleaned up nicely in order to make garbage collection possible, and removing listeners you added is just one of those? Or am I missing something here. If I was reading someone elses code and not seeing listeners cleaned up, I'd get nervous, since then I would have to check each addListener to see if the references were weak.
URL: http://blog.objectpainters.com
Sorv,
I definitely think developers should explicitly clear their listeners, but it's something that is very frequently missed, even by the best AS developers. Always using weakly referenced listeners is a good way to ensure that if you do miss cleaning one up, it isn't going to prevent collection.
Think of it as turning on the alarm and locking your doors - it provides an added level of security.
Posted by: Grant Skinner at July 16, 2008 08:28 AMURL: http://gskinner.com/blog/