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

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.

Related

What can I use instead of Timers to help my performance?

My app is very slow on mobile devices.
It uses a lot of Event.ENTER_FRAME event listeners, so I switched (as I read they are much more performance heavy) to a global timer that these classes listen for if they need to update (they only use these timers if they have been initiated by a TouchEvent).
I also tried enabling hardware acceleration, and switching to using the CPU, but these have not helped with the lag issues to a noticable amount. (my app has very few animations so I think this is the best decision)
I do have a lot of MovieClips in it, however very few of them actually animate, so I doubt this is an issue. (This is also why I do not freeze and unfreeze objects, as they are never removed from the stage)
On this website they said the following about timers
– the more timer instances linger in the system, the more you can expect slow and choppy gameplay or unresponsive UI.
I only use one timer, so I didn't think this would be an issue, but I do have a lot of event listeners that use this timer to update, so I can only figure that the timer must be the issue. Thus to avoid lag I am looking for a better alternative.
To be clear, I'm not using many Event.ENTER_FRAME event listeners anymore, I've mostly switched to timers. With that being said, here is an example of a more resource demanding one.
package
{
import flash.display.MovieClip;
import flash.events.Event;
public class mc_BattleScene extends MovieClip
{
public static var mc_PlayerFace:MovieClip;
public static var enemyAttackEffect:MovieClip;
public static var mc_playerHitBox:MovieClip;
public static var battler1:Number = 1;
public static var battler2:Number = 1;
public static var battler3:Number = 1;
public var lvlModifier:Number;
public var dmgReduction:Number;
public var dmgDealt:Number;
public static var dmgSpeed:Number = 1;
public function mc_BattleScene()
{
visible = false;
addEventListener(Event.ENTER_FRAME, onFrame);
mc_PlayerFace = playerFace_mc;
enemyAttackEffect = attackEffect_mc;
mc_playerHitBox = playerHitBox_mc;
function onFrame(e:Event):void
{
battler1_mc.gotoAndStop(battler1);
battler2_mc.gotoAndStop(battler2);
battler3_mc.gotoAndStop(battler3);
if (Game.playerInteractionStatus[1])
{
//we are fighting
visible = true;
if (attackEffect_mc.hitTestObject(playerHitBox_mc))
{
// attack hit us, deal dmg
dmgReduction = (Game.playerStats[2] * (Game.enemyStats[1]));
dmgDealt = Game.enemyStats[1] - dmgReduction;
attackEffect_mc.x = 516;
if (Game.playerStats[0] - Math.round(dmgDealt) <= 0)
{
// round dmg to 0 (were dead)
Game.playerStats[0] = 0;
}
else
{
// deal damage to us
Game.playerStats[0] -= Math.round(dmgDealt);
}
}
else if(attackEffect_mc.hitTestObject(wall))
{
//stop the player from moving (by doing nothing)
}
else
{
attackEffect_mc.x -= dmgSpeed;
}
}
else
{
// reset the position of the attack effect if we are not fighting
visible = false;
attackEffect_mc.x = 516;
}
}
}
}
}
This example of Event.ENTER_FRAME is one of the few that actually are this complicated, the other Event.ENTER_FRAMEs tend to simply update values (ex: update a text field to display correct number variables)
For example: creating multiple vars inside an enterframe could clog
the memory and cause lag.
I've avoided doing stuff like this, as not only what you've said, but in my opinion I think it doesn't make sense to continuously define it in a function, I update these values only.
Resources
http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-8000.html
max links allowed

Haxe Map Memory Cleanup Issue

So I have been using Haxe for a while and it has occurred to me recently that I don't really get what happens on some other the non-flash targets as far as memory cleanup. I mean 'new'ing everything and dumping it by setting references to null gives me this feeling that there are memory leakages, but I can't seem to find the documentation I'm looking for.
Specifically I use dictionaries/maps a decent amount. Like this:
var items:Map<String, MyObject> = new Map();
items.set("someKey", new MyObject(args...));
// Later
items["someKey"].doSomething();
items["someKey"].setVal(2);
...
// When Finished
items.remove("someKey");
The last line there just dumps my object somewhere into oblivion and hopefully gets garbage collected (at least on the Flash target).
I put together a little program just to see the cleanup in action on Flash/Neko and then change it for other targets, but I am failing to even see the cleanup on the Flash Neko target. Here is the project code:
package;
import openfl.display.Sprite;
import openfl.events.Event;
import haxe.ds.StringMap;
import openfl.events.KeyboardEvent;
import openfl.ui.Keyboard;
class Main extends Sprite
{
private var keypressID:Int;
private var itemID:Int;
private var dict:StringMap<Sprite>; // Using this since I read Map<String, T> just compiles to StringMap<T>.
public function new()
{
super();
addEventListener(Event.ENTER_FRAME, init);
}
private function init(event:Dynamic):Void
{
removeEventListener(Event.ENTER_FRAME, init);
// Entry point.
keypressID = 0;
itemID = 0;
dict = new StringMap();
stage.addEventListener(KeyboardEvent.KEY_UP, keyPress);
}
private function keyPress(event:Dynamic):Void
{
if (Std.is(event, KeyboardEvent) && cast(event, KeyboardEvent).keyCode == Keyboard.A)
{
trace('ID: $keypressID - Adding Item');
keypressID += 1;
for (i in 0...10000)
{
itemID += 1;
dict.set('$itemID', new Sprite());
}
}
else if (Std.is(event, KeyboardEvent) && cast(event, KeyboardEvent).keyCode == Keyboard.R)
{
trace('ID: $keypressID - Removing Items');
keypressID += 1;
removeItems();
}
// Force garbage collector to run.
else if (Std.is(event, KeyboardEvent) && cast(event, KeyboardEvent).keyCode == Keyboard.C)
{
trace('ID: $keypressID > Starting GC');
keypressID += 1;
forceGarbageCollection();
}
}
private function removeItems()
{
trace('ID: $keypressID > Remove All Item');
for (val in dict.keys())
{
dict.remove(val);
}
dict = new StringMap();
}
private function forceGarbageCollection():Void
{
neko.vm.Gc.run(true); // This does not work.
}
}
I run this on Windows and under task manager, my neko process only grows and never shrinks. Its gets up to 500MB quick when hitting 'A'. I then 'R' to remove all references to the items, but they never get collected it seems even when I force the GC.
I also tried storing openfl.util.Timer objects with event listeners attached to them to do traces and they never seem to get collected either. They just keep tracing. Now I suspect that may be because of the event listener reference, but am sure I have seen that trick in other AS3 memory leak tracking code.
Am I missing something or doing something wrong?
Edit:
I have modified the above question to reflect this, but I was mistaken about Flash player. I did get the memory to be reclaimed when running in the Flash player using flash.system.System.gc(); It seems the problem may be specific to neko which my question still addressed.

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

Is it faster to set a reference to a local variable of a nested object in ActionScript or the same?

I have an object that is nested several levels deep which I'm referencing multiple times. Is it faster to create and set a reference to that object or does it matter?
Context
I heard that it is faster to create a local reference. This is years ago and for a Visual Basic project. But this is Flash. And the output is a SWF / bytecode. And the compiler could look at that reference and do what I'm doing so that the object does not have to be looked up each time.
For example:
public function doStuff():void {
Model.instance.view1.button1 = button1;
Model.instance.view1.button2 = button2;
Model.instance.view1.button3 = button3;
Model.instance.view1.button4 = button4;
Model.instance.view1.button5 = button5;
Model.instance.view1.button6 = button6;
// more fake code referencing more something.something.something like things
for (var i:int;i<something.something.something.length;i++) {
var result:Object = Controller.staticMethod(button1);
var result2:Object = Controller.staticMethod(button1);
}
}
Vs:
public function doStuff():void {
var view1:View = Model.instance.view1;
view1.button1 = button1;
view1.button2 = button2;
view1.button3 = button3;
view1.button4 = button4;
view1.button5 = button5;
view1.button6 = button6;
}
.
Taking Baris suggestion I tested it for myself. Below are the results (though I don't know how to interpret them into the actual difference ie 'method 1 is .000001 faster than method 2').
Results
Test 1
var instance:Model = Model.instance;
var startTime:int = getTimer();
for(var i:int = 0; i<10000000; i++){
instance.url = "";
}
trace(getTimer()-startTime); // 826, 929, 823
var startTime:int = getTimer();
for(var i:int = 0; i<10000000; i++){
Model.instance.url = "";
}
trace(getTimer()-startTime); // 3483, 3976, 3539
Test 2
var instance:Model = Model.instance;
var localLogo:BitmapImage = Model.instance.logo;
var startTime:int = getTimer();
for(var i:int = 0; i<10000000; i++){
localLogo = logo;
}
trace(getTimer()-startTime); // 2070, 2083, 2110
var startTime:int = getTimer();
for(var i:int = 0; i<10000000; i++){
instance.logo = logo;
}
trace(getTimer()-startTime); // 2028, 2509, 2154
Generally, the as3 compiler is not that smart as we think. I think it does that to reduce compilation times.
This page http://upshots.org/actionscript/20-tips-to-optimize-your-actionscript gives us a rule:
5: The one-dot rule When accessing nested variables, anytime a
variable requires even one level of scope shift to discover, and is
referenced more than once, save it to local variable. In drawing
classes, you’ll often see Math.PI / 2 referenced within loops that
might iterated hundreds of times each frame – that value should be
stored in a local variable. Note that this is not true when
referencing member methods. Saving Math.PI to a variable is
appropriate; saving Math.round to a variable is not.
It does make difference, but to a software building perspective, the more readable code would be the propper way to do it, unless it's critical code, like a physics section of your app.
Other than making time benchmarks, you can inspect the bytecode too, using this tool http://www.swfwire.com/
I prefer :
public function doStuff():void {
var view1:View = Model.instance.view1;
view1.button1 = button1;
view1.button2 = button2;
view1.button3 = button3;
view1.button4 = button4;
view1.button5 = button5;
view1.button6 = button6;
}
I think it looks cleaner, you can profile and see which one is faster but it won't matter performance wise if you are not doing this tons of times in some inner loop.
public function doStuff():void {
var startTime:int = getTimer();
for(var i:int = 0; i<1000000; i++){
Model.instance.view1.button1 = button1;
Model.instance.view1.button2 = button2;
Model.instance.view1.button3 = button3;
Model.instance.view1.button4 = button4;
Model.instance.view1.button5 = button5;
Model.instance.view1.button6 = button6;
}
trace(getTimer()-startTime);
}
public function doStuff():void {
var startTime:int = getTimer();
for(var i:int = 0; i<1000000; i++){
var view1:View = Model.instance.view1;
view1.button1 = button1;
view1.button2 = button2;
view1.button3 = button3;
view1.button4 = button4;
view1.button5 = button5;
view1.button6 = button6;
}
trace(getTimer()-startTime);
}
The time to access is probably irrelevant, because you seem to be doing some UI work, and fractions of milliseconds it will take are of no concern. There are other concerns though:
You are writing repetitious code - this is bad, always. If it must be repeated, you should write a program that repeats it from a single source everywhere, if it may not be repeated - opt not to repeat. The concern is simple. Once you discover that the buttons are in ModelB.instance.view1 you will need to copy and paste the same code multiple times - of course while doing repetitive work you will make a mistake.
AS3 code not only allows, it also welcomes side effects, which, in this case may create undesirable effect. For example, Model.instance.view1 may be a getter that returns a new copy of view every time it is accessed, or, when accessed, it may alter something else in the internal state of the instance, whatever that is, and performing this change multiple times is undesirable.
There's Demeter's rule. When simplified beyond possible, it says that you shouldn't use more than a single dot operator in succession. I.e. an object may only be aware of the property of its own property (or local variable), but never access the property of the property of the property and never further down the line. This imposes unnecessary restriction on the structure of your program. However, sometimes it looks like it might be difficult to avoid, you should strive for a better, self-contained code. More on Demeter's rule here: http://en.wikipedia.org/wiki/Law_of_Demeter
It is not a function of compiler to "optimize" such code, especially because of the side effects. Compiler will faithfully follow the bad code written by a programmer and generate a lot of repetitious assembly because it trusts the programmer that this code is actually needed. Note, I'm not talking about Adobe compiler, which is not an optimizing compiler, and wouldn't remove this code even if the static analysis suggested that. I'm talking about some abstract "ideal" compiler for the language, that would optimally compile the code in question.

Understanding Actionscript Garbage Collection

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