Tweens and Garbage Collection, Debugging Using System.gc()

Most often Tweens stop unexpectedly due to AS3 garbage collection. Unlike instances of the Timer class, Tweens might be eligible for garbage collection even if they are running. We show how to avoid the problem and how to use System.gc() method to debug your Tweens and make sure they behave correctly.

Download

Download all the well-commented source files corresponding to this tutorial, tweengc.zip.

The System.gc() Method

Garbage collection is an automatic, cyclical process of freeing memory by removing objects in an application that are no longer needed in the program. It is important to check if garbage collection works properly in your application. It can be done using, for example, System.totalMemory property. (See a How-To on that subject: Simple Code to Test Memory Consumption and Frame Rate of a Flash Movie.)

While it is important to check if garbage collection cycles properly, it is also worthwhile to check if no items are removed that might actually be still needed. Here the method System.gc() comes in handy. The method forces garbage collection, so you don't have to wait for the next automatic garbage collection cycle which may take a while if your application uses little memory.

The method works only in debug versions of the Flash Player, so we cannot demostrate it in SWfs running in a browser. According to AS3 documentation the method is: For the Flash Player debugger version and AIR applications only. In an AIR application, the System.gc() method is only enabled in content running in the AIR Debug Launcher (ADL) or, in an installed application, in content in the application security sandbox. If you download the files in the zip package linked below, open the fla files in Flash CS4 or CS5 and go to Control -> Test Movie. The buttons 'Force Garbage Collection' will work.

Tweens and Garbage Collection

The instances of the Tween class are especially vulnerable to garbage collection unintented by the programmer. A garbage-collected Tween simply quits ubruptly. This behavior is inconsitent with the behavior of Timers: a Timer that is running will not be garbage-collected. A running Tween might be.

To make sure that your Tweens are resistant to garbage collection, store them in global variables (or class-level variables in the case of a AS3 class). If you are creating dynamically a non-predetermined number of Tweens, push them into a global Array or a Vector. Below are code examples and screen shots of applications in the zip package that illustrate the issue and the solution.

Example 1

In this example, we have two rectangles which rotate at a click of a button with tweened rotation. For the red rectangle, the Tween is stored in a local variable, for the green rectangle the Tween is stored in a global variable. When the button that triggers garbage collection is clicked, the local Tween stops abruptly. You will find this example in the file tweenquit.fla in the zip package. Here are the most relevant parts of the code.

/*
We create two Shapes, block1 and block2. block1 is filled with red, block2 is green.
*/

var block1:Shape=new Shape();

this.addChild(block1);

block1.x=140;

block1.y=160;

var block2:Shape=new Shape();

this.addChild(block2);

block2.x=410;

block2.y=160;

drawBlocksAndLines();

var countLoc:int=0;

var countGlob:int=0;

/*
We create a GLOBAL Tween variable, twGlobal. Each Tween for block2 will be stored in this variable while each Tween for block1 will be stored in a local variable, twLocal, defined within the function 'doRotate' below.
*/

var twGlobal:Tween;

/*
The button 'btnCollect' forces garbage collection via the System.gc() method. The method works only in debug versions of the Flash Player. So it will work when you go to Control -> Test Movie but it will not work in a browser or in the stand alone player. (Within AIR environment, you may have to call the method twice.)
*/

btnCollect.addEventListener(MouseEvent.CLICK,onClickCollect);

 

function onClickCollect(e:MouseEvent):void {

System.gc();

//System.gc();

}

 

btnGoTween.addEventListener(MouseEvent.CLICK,onClickTween);

 

function onClickTween(e:MouseEvent):void {

doRotate();

}

/*
We define Tweens within 'doRotate' function. One is stored in a global variable, one in a local variable within the function. We attach event listeners to both Tweens (twLocal and twGlobal) to show that event listeners do not prevent a Tween stored in a local variable to be garbage collected.
*/

function doRotate():void {

countLoc=0;

countGlob=0;

block1.rotation=block1.rotation%360;

block2.rotation=block2.rotation%360;

var twLocal:Tween=new
        Tween(block1,"rotation",None.easeInOut,block1.rotation,360,72);

twLocal.addEventListener(TweenEvent.MOTION_CHANGE,localGoing);

twLocal.addEventListener(TweenEvent.MOTION_FINISH,localFinished);

twLocal.start();

twGlobal=new Tween(block2,"rotation",None.easeInOut,block2.rotation,360,72);

twGlobal.addEventListener(TweenEvent.MOTION_CHANGE,globalGoing);

twGlobal.addEventListener(TweenEvent.MOTION_FINISH,globalFinished);

twGlobal.start();

}

 

function localGoing(e:TweenEvent):void {

countLoc+=1;

redTxt.text=String(countLoc);

}

 

function localFinished(e:TweenEvent):void {

redTxt.text="Red finished";

}

 

function globalGoing(e:TweenEvent):void {

countGlob+=1;

greenTxt.text=String(countGlob);

}

 

function globalFinished(e:TweenEvent):void {

greenTxt.text="Green finished";

}

 

function drawBlocksAndLines():void {

..........

..........

}

Note: If you replace Tweens by Timers in the above code, Timers that are running will not be garbage collected, regardless if they are stored in a local or a global variable. To see that, check out the file timergc.fla in the zip package.

Example 2

It is especially likely to use local variables if you are defining dynamically a non-predetermined number of Tweens. This is what happens in our second example, tweenvec.fla. At each click of a button, we choose randomly a random number of squares in the grid and rotate them with a Tween by 360 degrees. In order for the Tweens not to be garbage collected, they have to be pushed into a global Array or a Vector. Here are the most relevant parts of the Timeline code in tweenvec.fla.

We create a GLOBAL Vector variable to store dynamically generated Tweens:

var tweenGlobVec:Vector.<Tween>=new Vector.<Tween>();

Here is the function 'doRotate' that runs when 'Click To Start Tweens' button is clicked. (The function 'getIndices' called within 'doRotate' generates random indices for squares to be rotated.)

function doRotate():void {

var tw:Tween;

var i:int;

var indVec:Vector.<int>=getIndices();

for(i=0;i<numTiles;i++){

tilesVec[i].rotation=tilesVec[i].rotation%360;

}

for(i=0;i<indVec.length;i++){

if(tilesVec[indVec[i]].rotation==0){

tw=new Tween(tilesVec[indVec[i]],"rotation",None.easeInOut,0,360,72);

//tweenGlobVec.push(tw);

tw.addEventListener(TweenEvent.MOTION_CHANGE,tweenGoing);

tw.addEventListener(TweenEvent.MOTION_FINISH,tweenFinished);

tw.start();

}

}

}

If the commented out line within the function remains commented out, Tweens will be garbage-collected. If you uncomment the line, Tweens will not be garbage-collected. (The listeners 'tweenGoing', 'tweenFinished' are defined in the script; they do not do anything significant.)

In the examples above, we looked at Timeline code. Not much changes if the code is within an AS3 class. A 'global' variable becomes, of course, a ' class-level' variable. You will find examples in the zip package as well.

A 'real-life' example in which we ran into Tweens that quit abruptly can be found at Moving Rectangles - Flash Generative Art Effect.

Back to AS3 How-Tos and Tips              Back to Flash and Math Home

We welcome your comments, suggestions, and contributions. Click the Contact Us link below and email one of us.

Adobe®, Flash®, ActionScript®, Flex® are registered trademarks of Adobe Systems Incorporated.