The Situation

Your Tweens aren't finishing. Or your URLLoaders won't load. Or your Timers stop ticking.

The Problem

Consider the following function:

function doStuff():void
{
	var x:Object = {};
}

When this method is called, it creates an object and stores a reference to it in the variable x. After the function finishes executing, there are no remaining references to the object, so the Flash Player clears it from memory (as soon as it gets around to it). Of course, this happens no matter what type of object we create.

Unfortunately, there are some situations where this behavior can cause major problems. Objects like Tween, Loader, and Timer are associated with asynchronous processes (animating, loading, etc..) that we expect to complete regardless of whether the objects that originated them are still in the player's memory.

Sometimes, we're lucky. Depending on the how many things your application is trying to do at once, the garbage collector might not perform a sweep while our Tweens are playing (or our URLLoaders are loading). Still, it's impossible to anticipate exactly when the garbage collector will do its thing, and we certainly can't allow it to decide whether our applications will function correctly or not. Therefore, we can never ever use these classes without storing a reference to each instance we create.

However, when the number of objects is dynamic (or you're simply making a lot of them), this can get to be a hassle pretty quickly. This problem is illustrated by the following (admittedly outlandish) function, which creates 1000 Sprites and uses 1000 Tweens to fade them out.

function doSomething():void
{
	var mySprite:Sprite;
	for (var i:uint = 0; i < 1000; i++)
	{
		mySprite = new ItemInTheLibrary();
		this.addChild(mySprite);
		mySprite.x = Math.random() * this.stage.stageWidth;
		mySprite.y = Math.random() * this.stage.stageHeight;
		new Tween(mySprite, 'alpha', None.easeNone, 1, 0, 1, true);
	}
}

In its current state, this function just won't work. (At least not on a MacBook Pro with 2GB of RAM.) All of the Sprites will be created and added to the stage, but few (if any) of the Tweens will play. By not storing any references to our Tweens, we authorize the garbage collector to dispose of them as soon as the Flash Player needs the memory for something else. Pushing each Tween instance into an Array would solve our problem (try it), but then we'd have to worry about removing them from the Array when the animation finished (or else leave 1000 unused Tweens hanging around in our computer's memory).

The Fix

In almost every conceivable use of Loader, URLLoader, and Timer, there should be no difficulty in maintaining a reference to your instance. Because you'll rarely need to make many instances of each class at once, storing them in a private property will usually suffice. (Don't forget to null the reference when you're done with it!)

private var _timer:Timer;

private function _startTimer():void
{
	this._myTimer = new Timer(1000, 1);
	this._myTimer.addEventListener(TimerEvent.TIMER, this._timerHandler);
	this._myTimer.start();
}

private function _timerHandler(e:TimerEvent):void
{
	// Remove the listener
	e.currentTarget.removeEventListener(e.type, arguments.callee);
	
	// We're done with the timer. Let it die.
	this._myTimer = null;
}

Tweens are a slightly different beast. Luckily, there are many fine tweening libraries available. It's no coincidence that these libraries usually allow you to create tweens using static methods (for example, Tweener.addTween()); after all, there's no danger in Flash garbage-collecting your instances if you're not dealing with any instances.

If, however, you've got a big project that's already using Adobe's classes (without maintaining references to your instance), you've probably come here in search of The Quick Fix. For that, I'm making available a set of classes (GCSafeAnimator, GCSafeLoader, GCSafeTimer, GCSafeTween, and GCSafeURLLoader) that prevent themselves from being garbage collected while their respective processes are underway. Each class has the exact same API as its Adobe counterpart, so to create the Tween in the above function, for example, you would type:

new GCSafeTween(mySprite, 'alpha', None.easeNone, 1, 0, 1 true);

Similarly, loading a url would look like this:

var ldr:GCSafeURLLoader = new GCSafeURLLoader();
ldr.load(new URLRequest(‘http://exanimo.com'));

To further facilitate search-and-replacing, the packaging is also based on that of the Adobe classes (so GCSafeURLLoader is located at com.exanimo.net.GCSafeURLLoader and GCSafeTween is located at com.exanimo.transitions.GCSafeTween). When an object is no longer performing its task (be it tweening, loading, or whatever), it again becomes eligible for garbage collection.

Download the GCSafe examples and source as a zip file or from my as3 subversion repository (AS3 only). They're available under the MIT License.

Ultimately, having objects that manage their own memory is not the best idea. While these classes fulfill the expectations that you probably had about Adobe's counterparts, I would not recommend using them on new projects. Instead, it's better to understand why your objects were being garbage collected in the first place and take steps to avoid it.


Comments

Gravatar geng

Hi Matt,

Thanks for the info! I am curious. Why do you declare the Dictionary object in GarbageCollectionShield class at compile time instead of at run-time?

Geng

Gravatar matthew

You mean why is it static? That’s a good question. I actually wasn’t sure about how to treat that, and it may change. However, there’s only one situation in which it matters: when one Object is added to multiple instances of GarbageCollectionShield, and then removed from one instance. Should the Object then be freed for garbage collection? Or must it be removed from every instance of the GarbageCollectionShield to which it was added?

When I made these classes available, I kind of figured that GarbageCollectionShield was a class only used behind-the-scenes and it didn’t much matter, but any thoughts on its API are certainly welcome. If the consensus is that it should handle things differently, I’ll change it.

Gravatar soulwire

Hello. Great work!

I have been looking through your classes, and theoretically there should be no noticable memory increase through using your shield. I was wondering tough, have you done any tests?

I have been looking at this thread: http://www.kirupa.com/forum/showthread.php?t=275524 perhaps it is of some relavence for an updated class which is both noticably faster for events and shields from the GC?

Good work again, thanks for sharing :)

Gravatar Dan

Thaaaaaaaaaank-you!

Am new to flash, but not to programming, so when my first piece of development work started randomly falling over, I didn't really know what to do. If it works once, it should continue to work! Wasn't thinking about garbage collection though :S

But, I'm amazed that it's a viable scenario for Adobe that Tweens etc can be garbage-collected before they have finished playing. At the least you would think they would feature a fairly prominent warning in the documentation, but there you go.

Anyway, great work. More people need to know about this though so I shall add Google-fodder in the form of my first search: "why won't my stupid tweens work". Hopefully some more desperate souls will stumble upon your work.

Cheers!

Gravatar Phillip Knezevich

This fixed my problem. Nice work.

Why the hell haven't Adobe deal with this..

PK

Gravatar Doug

Thank You! I've been looking for a solution for that for weeks!

Doug

Gravatar Tomek

It was really easy to apply the changes in my code.

Thanks

Gravatar Darkriderdesign

Dude thank you, this has been driving me completely crazy lately. I have seen a lot of online examples on how to fix this, but none of them seemed to work across the board for many situations. It seems Action Script 3 has now become very powerful, but at the cost of us all having to relearn some of the basics.

Rockin thanx again,

Darkrider

Gravatar Matthew Trainer

Thanks a ton! This saved me some massive headaches I've had for weeks.
You da man!

Matt

Gravatar Darcey

Well it may be just me but I see a logic as to why it wouldn't work.

Creating a loop which:

1. creates an object using a static name (never changes)
2. create a tween and apply to the static object name

Repeat

The problem is the use of the static object name. I'm supprised that any tweens work apart from the last 1 in the loop.

The displayObject works differently and thus adds objects (sprites in this case) in a different method than how the tween class works. Probably tracing out indexes and display class object ids and names would give a better picture as to what is going on.

Solution: Give each tween it's own object name to attach itself to.

import fl.transitions.*;
import fl.transitions.easing.*; // Can be removed if fl.motion.easing is left in
import fl.motion.easing.*; // Can be removed if fl.transitions.easing is left in

function doSomething():void
{

var mySprite:Sprite;
var myArray:Array = new Array();

for (var i:uint = 0; i < 1000; i++)
{
myArray[i] = new mcBox();
this.addChild(myArray[i]);
myArray[i].x = Math.random() * this.stage.stageWidth;
myArray[i].y = Math.random() * this.stage.stageHeight;
myArray[i].tween = new Tween(myArray[i], 'alpha', None.easeNone, 0, 1, 1, true);
}
}

doSomething();

Gravatar matthew

@Darcey:
Yes, there is a logic to why my loop doesn't work, but I'm not sure you've gotten it quite right. As I explain in the article, it's the logic of garbage collection; to make sure that each tween completes, you must ensure that there's a reference to each until they've finished. Therefore, it doesn't really have anything to do with "using a static name" per se, but the number of variable references.

At first glance, your example seems a lot like my suggestion to "[push] each Tween instance into an Array"; however, because your Array is defined inside the function (and no references to it are stored after the function completes), it too would be garbage collected, leaving no references to your Tweens. Without any references to them, they would be snatched up by the garbage collector and stop working.

Of course your code does work. Why? Well, if you examine it a bit closer, you'll realize that you're in fact not storing references to the Tween in an Array. Instead, you're storing your reference on an item in the Array – namely, your Sprite! The display list contains a reference to your Sprite, since it's been added to the stage, and your Sprite in turn contains a reference to your Tween. Thus, it's protected from garbage collection. The Array, however, is not, and will be garbage collected after the completion of the function. In other words, the Array isn't even used and your function is exactly equivalent to this:

function doSomething():void
{
  var mySprite:Sprite;
  var myArray:Array = new Array();

  for (var i:uint = 0; i < 1000; i++)
  {
    mySprite = new mcBox();
    this.addChild(mySprite);
    mySprite.x = Math.random() * this.stage.stageWidth;
    mySprite.y = Math.random() * this.stage.stageHeight;
    mySprite['tween'] = new Tween(mySprite, 'alpha', None.easeNone, 0, 1, 1, true);
  }
}

This technique works and was used by many in AS2 programming. However, it really isn't very good practice for a few reasons. First, notice that I don't use dot syntax to access the tween property of mySprite. That's because Sprite is a non-dynamic class with no tween property, so the compiler would throw an error if I tried. This is kind of a hack to bypass the rules that the class defines. Your example does the same thing, but it's somewhat hidden: since accessing items in an array returns an untyped reference (*-typed), you can set any property you'd like on the item, even if it's not defined by the class. Secondly, setting the tween property on your sprite could have unintended consequences. Suppose elsewhere in your application, you (or somebody else who isn't aware of what you did here) use the same technique to set a Tween on a different property? The tween property of your Sprite would then point to this new Tween, and there would be no reference to the original alpha Tween. The sprite would stop fading and the developer would be left with the inenviable task of determining why one Tween should cause another to stop sporadically.

Hopefully, this will clear some things up about the original article, and about garbage collection in general.

Gravatar Darcey

Yep…

Just placing the tweens into an array for seperation purposes and lets them be garbage collected on completion.

For more complicated usage I wouldn't go anywhere near this code…..

Not sure about attaching the tween to the spirte object, but it all works :)

Something I tinkered up a while back and posted up on my site for online reference which may be of use to people reading this page.

http://www.allforthecode.co.uk/forum/viewtopic.php?f=8&t=166

Gravatar matthew

Actually, "placing the tweens into an array" does not allow them to be garbage collected upon completion. Even after they've completed, the Array will still contain a reference to them. Therefore, they won't be garbage collected and will remain in memory.

In any case, this isn't what your code is doing. As I tried to explain above, your code is in fact "attaching the tween to the sprite object" – not "placing the tween into an array."

The Sprite is created and stored in an Array in the following line:

myArray[i] = new mcBox();

At this point, the Sprite is stored at myArray[i]. Any occurrences of myArray[i] are simply referencing that Sprite. So when you type the following:

myArray[i].tween = new Tween(myArray[i], 'alpha', None.easeNone, 0, 1, 1, true);

what you are actually doing is 1) retrieving a reference to your Sprite, 2) creating a Tween instance, and 3) storing a reference to the Tween on the Sprite object itself. This is exactly the same as the following line in my comment above:

mySprite['tween'] = new Tween(mySprite, 'alpha', None.easeNone, 0, 1, 1, true);

If you're still not convinced, try adding the statement trace(this.getChildAt(this.numChildren - 1)['tween']); in your for loop. This will trace the tween property of the most recently added Sprite.

As I mentioned above, there are several reasons why this would not be a good idea. One that I didn't mention (but that your comment has reminded me of) is that none of these Tweens will be garbage collected until the Sprite they're acting on has been removed from the stage and all references to it are lost. (Again, it's the Sprite instance that's keeping your Tween from being garbage collected so as long as the Sprite exists, so will the Tween - unless, of course, you overwrite the Sprite's tween property.) That means that 1000 unused Tweens will be taking up space in memory!

Does this make things any clearer?

Gravatar Darcey

I'm fine with the code, my code was just a quick response for others to hot wire, try out, modify, discuss etc.. Also a quick method of getting the original example running. Thought some may find it interesting and also beneficial especially with your replies and breakdowns of the code posted. :)

If I were to code something up for this purpose I would probably be stemming everything from:

private var mcArray:Array;
private var mcTweenArray:Array;
//Maybe even use a multi dimensional array for a single array reference, all depends on the coders pref'

Then manage what the target functionality is from there, with probably stage / status variables for position in the tween animation stages for flow control with possible interactivity disabling while tween is in progress etc.

Gravatar Nick

I don't think this is accurate. Your example doesn't work because the Tween class explicitly doesn't actually create its internal Timer until "later", and even when it does, it adds its event listener using a weak reference. If you, for instance, modify the Tween class to add its listener in the constructor, AND make it non-weak-referenced, AND start the timer immediately, then it will work.

Basically what's happening is, the Tween class is explicitly not letting the system keep it alive. If the Timer is started and the event listener is not weak, then the class will (correctly) survive.

I think this is a fault of the Tween class – its design means you have to store a reference to it for it to work reliably.

Gravatar matthew

Actually, the Tween class does create and start its internal Timer immediately. The constructor creates the Timer on line 433 and starts it on the next line via a call to start, which in turn calls startEnterFrame, which starts the Timer. Therefore, the only part of your scenario that's necessary is to "make [the listener] non-weak-referenced". In this situation, that means that Flash will keep a reference to the the instance's timerHandler or onEnterFrame method (depending on the value for useSeconds). Since the methods store an internal reference to the instance to which they belong, Flash maintains a reference chain to the Tween instance itself, preventing it from being garbage collected, as I described in the article.

So I'm not exactly sure what part of the article you think is inaccurate. I agree with you when you say "the Tween class is explicitly not letting the system keep it alive" and "its design means you have to store a reference to it for it to work reliably" (I said the latter almost verbatim in the article — in bold no less!). However, as I explain above, the reason that this happens is because no reference to the Tween instance is maintained. There are many ways to alleviate this issue (i.e. store a reference to the Tween): add one of its methods as a listener to another object's events (as per your suggestion), store it as a static property on a class (similar to how most 3rd party Tweening libraries work), put each instance into a Dictionary (like the GCSafe classes I provide), etc. It doesn't matter; they're all doing the same thing.

However, IMO, tinkering with the Tween class as you suggest is a very bad idea. It violates the open/closed principle and requires those using your code to know that they must use a modified version of the bundled Tween class. Somebody with a fresh install of Flash will be able to compile your project, but their Tweens won't work! They'll have no way of knowing that they need to dive into Adobe's classes and tinker with stuff. Conversely, by extending the class (as I did with GCSafeTween), everything will either work smoothly (if they have the GCSafeTween class), or they'll get a compile error telling them they need the GCSafeTween class. Both of these are much preferred.

Whether or not this is a fault of the Tween class is open for debate. Most, I'm sure, will agree with you, but there is definitely an argument to made to the contrary.

Gravatar ed

you rule!

Gravatar Eric Hague

Thank you so much. I've been having this problem ever since I started using the Tween class and was all set to make a human sacrifice to the Flash Gods when I came across your work.

Gravatar viva

hi, I've been coding using action script 3 and using the TransitionManager class to do some effects to my objects. Unfortunately, sometimes the effects just stop in the middle (incomplete) e.g a bouncing effects, or iris effects. Do you think this is caused by the GC?

Gravatar James

Hi

I didn't use your classes but you did help me figure out why my Tweens were randomly crashing.

Thanks a bundle!

James

Gravatar Anthony Brady

I have an XML slideshow that infinitly loops through various swf files which in turn have their own xml data feeds for things like phone stats etc. The problem I have is that the memory of the flash player increases everytime an swf file is loaded in. I have downloaded your packages and used your methods and the memory still grows, any ideas?

Gravatar Pedro Mendes

You're the boss!!! You solved the biggest headache in my project! Thanks a million and keep up the good work!

Gravatar matt

holy moly
thank u soooo much
yet another example of how either:

adobe flash employees are overpaid
or
macromedia left a legacy of poor code
or
both

regardless….u saved my butt major…..
u rulz

Gravatar Flash Tutorials | AS3 Classes Roundup Part2 | Lemlinh.com

[...] off, I will say that without Matthew Tretter’s garbage collection classes this demo would not be possible…While generating this many “particles” on ENTER_FRAME, the [...]

Gravatar PerfectlyNormal

Awesome! This problem has been a real pain in the ass for a long time now. Assumed it was I who did something wrong, so never even tried to google for it. Thanks a lot for these fixes

Gravatar Nymite

This is amazing! I'm working on a rather complex project right now (with multiple tweens and loading events happening at the same time) and the classic "tween choke" has been driving me nuts. Your solution has just made my life so much easier. Thank you!:)

P.S. You should really place a PayPall "Donate" link somewhere :).

Gravatar Token

Great work! And great explanation of the problem: I used to think, it was my CPU or memory, being flood with tweens, stopped process some of them…)

Gravatar Duncan Reid : hy-brid.com » Simple AS3 ToolTip

[...] in lieu of the built in Tweening methods due to problems in the past, you can read more about it on Matthew Tretters Blog as he provides an indepth explanation and examples as to why this might be [...]

Gravatar Daniel Dowell

I must say that even though this seems to cause a lot of headache for a lot of people, your classes are actually causing more harm than good. You are leading yourself and others down a path to non-object oriented programming where you no longer can trust which objects are eligable for GC and which arent. Adobe knew very well what they were doing when they designed AS3 this way. You are supposed to keep track of each Tween or URLLoader and remove the references to them when they are no longer needed. Think about this in an object-oriented way and you will soon understand that objects without parents are a bad idea.

Use arrays to store multiple refernces and remove the items that you no longer need when you no longer need them!

Gravatar matthew

@Daniel Yeah, I agree. When I wrote this article I was WAY overzealous about pimping my code. Truth be told, I haven't used these classes in… well I don't even know how long. I've been meaning to update this article for ages now. Thanks for forcing my hand.

Gravatar Danno

I can't thank you enough for this. absolutely awesome.

Rocksteady,
Danno~

Gravatar max

the note abouts tweens getting garbage collected saved my recent flash project. thank you so much!

Gravatar Coke, popcorn ‘n’ Flash » Blog Archive » Set Ease value with ActionScript

[...] I've used not the standard Tween class, but a GCSafeTween class: if you don't store a reference to your tween the garbage collector may collect your tween, [...]

Gravatar Drew

Was this ever really an issue with Timer? I've read elsewhere that Timer objects are never gc'd so GCSafeTimer is really the same as the regular Timer. Am I wrong?

Gravatar matthew

@Drew Yeah, you're right. As long as the Timer is running, it won't be garbage collected. (You're probably remembering this article.) Pretty big bug, IMO, considering that (unlike with Tweens), people often create Timers that run until they are explicitly told to stop (i.e. Timers with a repeatCount of 0). That means you could have a lot of useless Timers clogging up memory without even realizing it. In my opinion, developers should treat this behavior as a bug, and never rely on it to prevent their Timers from being garbage collected.

Gravatar numediaweb

thank matt
saved me mounths of seraches of cause my project to bug!

thanx, keep on!

Gravatar manderson

Great work sir! This cleared up all the issues I was having.

Gravatar Joe

Matt!!
Thank you so much man you have really out done your self with this tween fix. I am extreamly greatful

Gravatar cem yildiz

thank you so much. it's a great library

Gravatar aandyy

thanks so much! this was driving me crazy and i didn't want to have to push everything onto an array like the other suggestions i've read…glad to know it wasn't just me!

Gravatar Elliot Rock

Mate please please add WEAK references in you event listeners, your OP is wrong:

this._myTimer.addEventListener(TimerEvent.TIMER, this._timerHandler);

Should be:

this._myTimer.addEventListener(TimerEvent.TIMER, this._timerHandler, false, 0, true);

Also just don't bother with the crap native tween class it is bogus on so many levels. Use TweenLite or TweenMax.

Gravatar Karl

That sure saved my day and a lot of time.

A very grateful thank you.

Gravatar Mandeep

thanks, you are save my life!

it's really useful and really easy to use!

Thanks

Gravatar Tim

I've always hated flash, always. Coming from a C# background using Visual Studio IDE to flash shows me how much I rely on a decent intellisense and verbose error messages / debugging. Well I've finally been forced to use flash on a project and was starting to really get into it when all of the sudden my tweens weren't finishing. I had maybe 30 objects with 1 or 2 tweens each, not even running at the same time, and it was freaking out every 2nd or 3rd reload.

This awesome post has quelled my anger for now, but it still boggles my mind that this could even happen.

Thanks a million!

Gravatar Luca

Y O U A R E M Y S A V E R !!!!!!!!!!
THANKSSSSSSSSSSSSS A LOT!!!!!!!

Gravatar Kirk

You have no idea how much you have just completely and utterly saved my ass. Very large flash project that has had those random stops are now fixed thanks to you. Thank you, thank you, thank you.

Gravatar MMnemonic

T-H-A-N-K U!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Thank you a million times!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Gravatar Angry_Ookami

I love you Man :')

Gravatar Gin

Hi , I am new to as3 .What is the difference between "tween class" and "TransitionManager class"? Which is better to use?

Gravatar J Richardson

Thank you so much! This really saved my ass!