Understanding Actionscript Garbage Collection - actionscript-3

in an attempt to see and hopefully understand actionscript's garbage collector, i've set up a sample project that loop-tweens the value of a pixel bender parameter on stage.
my first concern was the amount of memory that was being used at launch (~26 MB). while i like to believe i'm cautious about memory by removing event listeners and nulling useless objects for the garbage collection as much as possible, i also believe i don't fully grasp where, why and when it works.
the trace of the total system memory displayed a steady rise starting from around 26 MB until around 28 MB after about a minute later (or so). suddenly it's plummeted down to 25 MB only to continue rising once again. this seems to cycle over and over.
here are some questions that come to mind:
1. is there a general time delay for the garbage collector?
2. does it activate after a certain amount of memory has been allocated?
3. can objects be explicitly removed immediately without relying on the garbage collector?
4. what is an acceptable range for memory usage when running flash?
attached is my code.
import fl.transitions.*;
import fl.transitions.easing.*;
var shader:Shader;
var shaderFilter:ShaderFilter;
var motionTween:Tween;
var filterParameter:Number = 0.0;
var loader:URLLoader = new URLLoader();
var phase:Boolean = false;
loader.dataFormat = URLLoaderDataFormat.BINARY;
loader.addEventListener(Event.COMPLETE, dataLoaded);
loader.load(new URLRequest("myBoringFilter.pbj"));
function dataLoaded(e:Event):void
{
loader.removeEventListener(Event.COMPLETE, dataLoaded);
shader = new Shader(e.target.data);
shaderFilter = new ShaderFilter(shader);
flower.filters = [shaderFilter];
tweenLoop(null);
}
function tweenLoop(e:TweenEvent):void
{
if (motionTween != null)
{
motionTween.removeEventListener(TweenEvent.MOTION_CHANGE, updateFilter);
motionTween.removeEventListener(TweenEvent.MOTION_FINISH, tweenLoop);
motionTween = null;
}
phase = !phase;
if (phase == true)
{motionTween = new Tween(this, "filterParameter", Regular.easeOut, filterParameter, 100.0, 2.0, true);}
else
{motionTween = new Tween(this, "filterParameter", Regular.easeOut, filterParameter, -100.0, 1.0, true);}
motionTween.addEventListener(TweenEvent.MOTION_CHANGE, updateFilter);
motionTween.addEventListener(TweenEvent.MOTION_FINISH, tweenLoop);
}
function updateFilter(e:TweenEvent):void
{
shader.data.amount.value = [filterParameter];
flower.filters = [shaderFilter];
//Update Memory
trace("System Total Memory: " + System.totalMemory);
}

This is generally the most common resource for this question:
http://www.gskinner.com/blog/archives/2006/06/as3_resource_ma.html

Related

AS3 Memory Management reverse engineering

Has anyone figured out how AS3 actually handles garbage collection? I'm having so many issues releasing memory in a game I'm developing.
Made a small demo:
public class MemoryTesting extends Sprite
{
static protected var list:Array = null;
public function onKeyDown(event:KeyboardEvent):void {
if( event.keyCode == 65 ){ //A key - adds memory
if( list == null ){
list = [];
for( var index:int = 0; index < 10000000; ++index ){
list.push(new Matrix3D());
}
}
}
else{ //Any other key removes memory.
if( list ){
var size:int = list.length;
for( var index:int = 0; index < size; ++index ){
list.pop();
}
list.length = 0;
list = null;
}
System.gc();
}
}
}
Running Flash Player Debugger stand-alone 11.4r402 in Windows 7. Watching the Task Manager, with no keys pressed, the debugger sits idle at 11,000 K.
Pressing a (adding 10Mil Matrix3D classes) takes it up to 962,000 K.
Pressing another key (removing references to the Matrices and nulling the array) depends on how many times I press it.
The first time we call GC - drops to 255,000 K.
The second GC call - 92,000 K.
Third - 52,000 K.
Forth - 42,000 K.
Fifth - 39,000 K.
Sixth & any consecutive times after sits at 38,000 K.
I hear people talking about the GC waiting for "opportune times" to collect. But this is an empty application, not even a enter_frame event and there is no amount of time you can leave it idle for it to remove the remaining 27,000 K (38,000 - 11,000).
Sitting at the new low, if we re-add the matrices we move back up to 975,000 K.
That is to say, 13,000 K more than the first time. If I repeat this add/remove, it stays the same, going back up to 975,000 K and down to 38,000 K.
Remember, there is nothing going on in this application. My actual application has 650mb of raw bitmap data, let alone 100mb of SWF to parse and 500mb of XML classes that I only use in initialisation code.
I've read multiple times that even calling GC manually is bad, let alone 6 times. But none of the Matrix3D's will be released if I don't.
How does anyone handle this? Shall I just call GC 6 times at the end of initialisation?
Edit:
I was testing in release mode for differences and whether, without the System.gc() call, that if it doesn't free the memory from flash, at the least re-uses it properly. It does eventually, but with a new, higher footprint. With a full list sits at 990,000 K, clearing it takes it to 1,050,000 K.
This is for data that initially cost us 962,000 K RAM. That's 90MB of weird internal flash GC memory. Let alone ignoring that it won't ever give the memory back to the OS (without the explicit GC calls).
Actionscript's GC is weird, nothing to say,
If you'll try to use something like this, it helps (I just tested and GC clears out the memory on the very first try (key click)), Just changed Array to Vector to test more quicker, just the same should happen with Array too. My enviroment is FlashCC in this case.
package
{
import flash.display.Sprite;
import flash.events.KeyboardEvent;
import flash.geom.Matrix3D;
import flash.net.LocalConnection;
import flash.system.System;
import flash.utils.setTimeout;
public class MemoryTesting extends Sprite
{
var list:Vector.<Matrix3D> = null;
function MemoryTesting()
{
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
}
public function onKeyDown(event:KeyboardEvent):void
{
var matrx:Matrix3D;
var index:int
if (event.keyCode == 13)
{
trace(System.totalMemory, "starting to fill...")
if (list == null)
{
list = new Vector.<Matrix3D>
for (index = 0; index < 1000000; ++index)
{
matrx = new Matrix3D();
list.push(matrx);
}
}
trace(System.totalMemory, " done...")
}
else
{
if (list)
{
trace(System.totalMemory, " preparing to delete...")
list.splice(0, list.length);
list = null;
}
//force GC to work normally, it really helps (at least in my case)
try
{
new LocalConnection().connect('foo');
new LocalConnection().connect('foo');
}
catch (e:*)
{
}
setTimeout(function()
{
trace(System.totalMemory, " deleted")
}, 50)
}
}
}
}
This strange snippet really helps on most cases
try {
new LocalConnection().connect('foo');
new LocalConnection().connect('foo');
} catch (e:*) {}
Here is the freat article:
http://gskinner.com/blog/archives/2006/08/as3_resource_ma_2.html

SampleDataEvent.SAMPLE_DATA - regulating how often this event is tirggered

I is to possible to regulate how often flash triggers SAMPLE_DATA event when sampling data from input device ? I need to sample constantly (even silence).
var mySound:Sound = new Sound();
mySound.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData);
AFAIK currently this function gets called 20 times/s
playing with the sample code from http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/events/SampleDataEvent.html
var mySound:Sound = new Sound();
function sineWaveGenerator(event:SampleDataEvent):void {
for ( var c:int=0; c<8192; c++ ) {
event.data.writeFloat(Math.sin((Number(c+event.position)/Math.PI/2))*0.25);
event.data.writeFloat(Math.sin((Number(c+event.position)/Math.PI/2))*0.25);
}
}
mySound.addEventListener(SampleDataEvent.SAMPLE_DATA,sineWaveGenerator);
mySound.play();
the less data you write, the more often the method gets called.
if you call it too often the sound may get clicky and gross. try adjusting the frame rate.

AS3: memory leak with two loaders

I have a AS3 file with only one frame. Here is the code :
stop();
trace("Debut du code.");
var chargeur:Loader = new Loader();
var chargeur2:Loader = new Loader();
var adress:URLRequest = new URLRequest("img/idle.swf");
chargeur.load(adress);
addChild(chargeur);
chargeur.contentLoaderInfo.addEventListener(Event.COMPLETE,isLoaded)
function isLoaded (evt:Event) {
trace("Loading complete");
var adress2:URLRequest = new URLRequest("img/oldcoucou.swf");
chargeur2.load(adress2);
addChild(chargeur2);
}
Problem is : there is a memory leak , something like 3mo/s... and I can't figure why and what I'm doing wrong. Any clues ?
Thanks.
Edit:
With only one loader, I don't have the memory leak. If I load two, then hide one (chargeur.visible=false or chargeur.y=1200), no memory leak either. The problem occurs only when there is 2 visible at the same time. That sounds crazy, I know... I was hoping it was a know bug or a mistake in the code...
Assuming the loaded .swf's don't include any memory leaks the following code should be leak free.
stop();
trace("Debut du code.");
var chargeur:Loader = new Loader();
var chargeur2:Loader = new Loader();
var adress:URLRequest = new URLRequest("img/idle.swf");
var adress2:URLRequest = new URLRequest("img/oldcoucou.swf");
chargeur.contentLoaderInfo.addEventListener(Event.COMPLETE,isLoaded, false, 0, true);
chargeur.load(adress);
function isLoaded (evt:Event):void {
chargeur.contentLoaderInfo.removeEventListener(Event.COMPLETE,isLoaded);
adress = null;
trace("Loading1 complete");
addChild(chargeur);
chargeur2.contentLoaderInfo.addEventListener(Event.COMPLETE,isLoaded2, false, 0, true);
chargeur2.load(adress2);
}
function isLoaded2 (evt:Event):void {
chargeur2.contentLoaderInfo.removeEventListener(Event.COMPLETE,isLoaded2);
adress2 = null;
trace("Loading2 complete");
addChild(chargeur2);
}
I think it is impossible.
I guess the swf one be loaded into your program, it immediately run itself's program, it were that
it load other swf , swf A load swf B, swf B load swf A ....... \n. it's like a endless recusion function causing the stack overflow.

AS3 Delay execution?

I'm querying Rotten Tomatoes' API for movies listed in an XML document. The problem I'm having is, whist iterating through and querying for each movie I'm hitting RT's API limit which, in turn, is throwing an IO error.
Is there a way I can delay execution in a loop for about a second or so, to avoid this?
Short answer: use a Timer.
Suppose you have 100 movies in an array:
var movies:Array;
And for each of these movies you want to call an API on the server. First I would maintain a variable to mark the current movie:
var currentMovieIndex:int = 0; // first movie
Then, assuming you're using URLLoader:
private function makeAPICall():void
{
// Construct URLRequest based on current movie object.
var urlRequest:URLRequest = getURLRequest(movies[currentMovieIndex]);
var urlLoader:URLLoader = new URLLoader();
urlLoader.addEventListener("complete", completeHandler);
urlLoader.load(urlRequest);
}
getURLRequest() being a function that gives you a URLRequest object for the movie you pass to it as an argument.
In your completeHandler():
private function completeHandler(event:Event):void
{
...
if (++currentMovieIndex < 100) {
var timer:Timer = new Timer(3000, 1); // 3-second delay
timer.addEventListener("timer", timerHandler);
timer.start();
}
}
In your timerHandler():
private function timerHandler(event:Event):void
{
makeAPICall();
}
What are you loading from the xml that is causing the error? XML should not take long to load. If your loading items contained in the xml then a timer will not be helpful. It'll take too long and give a bad experience. Say the user has a fast connection but still has to wait on a delay for each file. What I'd do is check to make sure I've loaded one file(loadBytes) before moving to the next file. You'd only have to do this with the largest file contained in each node should be enough.
That would be more flexibly dependent on the users connection.

What is/are the (dis)advantage(s) of using weak references in EventListeners in AS3?

I've been teaching myself actionscript 3 over the past month or so and recently ran into an issue where an object kept doing things after I thought it had been removed. I figured out that the problem was caused by an event listener using the default of useWeakReference = false and I'm wondering why that's the default.
what is the advantage to not using a weak reference? and why is that the default?
it seems to me that in general you'd want to use weak references, so I must be missing something.
Thanks,
-Ted
the point is, that weak references are expensive ... they are both slower and consume more space ... here is some benchmark code:
package {
//{ region imports
import flash.display.Sprite;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.system.System;
import flash.utils.*;
//} endregion
public class Main extends Sprite {
public function Main():void {
switch (0) {
case 0: this.benchmarkDispatchers(false); break;
case 1: this.benchmarkDispatchers(true); break;
case 2: this.benchmarkDictionaries(false); break;
case 3: this.benchmarkDictionaries(true); break;
}
}
private function benchmarkDictionaries(weakKeys:Boolean, size:uint = 1000000):void {
var a:Array = [];
for (var i:int = 0; i < size; i++)
a.push( { "foo":i } );
var d:Dictionary = new Dictionary(weakKeys);
var start:int = getTimer();
var mem0:int = System.totalMemory;
for (var j:int = 0; j < size; j++)
d[a[j]] = j;
trace("adding "+size+" keys took "+(getTimer()-start)+" msecs and "+(System.totalMemory-mem0)+" B of memory with weakKeys == "+weakKeys);
}
private function benchmarkDispatchers(weakRef:Boolean, size:uint = 100000):void {
var a:Array = [];
var f:Function = function (i:*):Function {
return function ():void { i; }
}
for (var i:int = 0; i < size; i++)
a.push( f(i) );
var e:EventDispatcher = new EventDispatcher();
var start:int = getTimer();
var mem0:uint = System.totalMemory;
for (var j:int = 0; j < size; j++)
e.addEventListener("foo", a[j], false, 0, weakRef);
trace("adding " + size + " event handlers took " + (getTimer() - start) + " msecs and " + (System.totalMemory - mem0) + " B of memory with weakKeys == " + weakRef);
}
}
}
this is, what i get on my machine:
adding 100000 event handlers took 679 msecs and 6922240 B of memory with weakKeys == false
adding 100000 event handlers took 1348 msecs and 13606912 B of memory with weakKeys == true
adding 1000000 keys took 283 msecs and 16781312 B of memory with weakKeys == false
adding 1000000 keys took 906 msecs and 42164224 B of memory with weakKeys == true
results are a little more drastic for dictionaries, most probably because there are no ActionScript calls involved, concerning time, and since some storage overhead in event handler registering lessens the difference between memory needed (as you can see, it's 69 Byte/handler and 16 Byte/key, when comparing weak references) ...
so yeah, it is about performance ... using weak references is not about the cool fact, that you don't have to remove the listener in order for an object to die ... if you want to have a scalable app, you need to do these kind of things yourself, and if you want it to be 100% reliable, you can't hope for the GC to do your job, but you need tomake cleanups yourself ... and also, if you have a good hierarchy in your app, you will probably not run into this problem much to often ... in a sense, it is a luxury, if you don't wanna spend you time doing proper object cleanups, because problems that cannot be solved without weak references are rare ... it should be used when it offers a real advantage, not just out of laziness ... i think that is why it is false by default ...
hope that helps ... ;)
greetz
back2dos
You're right, typically you do want to use weak references, probably about 99% of the time. I think the default value really should be set to true, but we just have to deal with it not being so. I suggest that you get in the habit of always using weak references, and you will save a few headaches, and see some performance benefits as it helps with garbage collection management.
So when would you want it to be false? Basically any time you don't want things automatically clean up when the garbage collector decides you're not using it anymore. These situations are rare, and when the time comes you'll probably just know.
HOWEVER That might not solve your particular problem on it's own. The garbage collector only runs at unpredictable intervals, so even though you remove all references to an object, it might still handle these events until it is cleaned up. Your best bet is to remove the event listeners yourself to guarantee they will not be triggered again. You can do that with the removeEventListener method.
Good luck,
Tyler.
Weak references can bite you when doing asynchronous operations, since anything that is not executing for tethered to the object graph can be garbage collected at any moment.
Particularly, never use weak references for event handlers that are scoped functions:
function weakRefSample() : void
{
var evntDispatcher : IEventDispatcher = new EventDispatcher();
evntDispatcher.addEventListener(Event.COMPLETE, scopeHandler, false, 0, true);
function scopedHandler(event : Event) : void
{
}
}
There are two things that go wrong in the above example:
scopeHandler can be collected because there it is not tethered to the object graph
the execution scope of weakRefSample can be collected because nothing requires it to exist (nothing is using scopedHandler), so evntDispatcher can be collected. If evntDispatcher was waiting on another asynchronous event, it might be collected before that event completes.
I have found that Timer instances which have weak referenced event listeners sometimes do not fire the event.