Can someone post an example of as3 code (specifically event listener included) that would be a simple example of something that could leak memory... also hopefully could you post a solution to the problem shown?
The question is: What is a simple example of leaking memory in an AS3 event listener and how can you solve it?
public class MySprite extends Sprite {
public function MySprite() {
if(stage) {
init();
} else {
addEventListener(Event.ADDED_TO_STAGE,init);
}
}
private function init(e:Event = null):void {
stage.addEventListener(Event.RESIZE,handleStageResize);
}
private function handleStageResize(e:Event):void {
// do some processing here.
}
}
Somewhere else:
var mySprite:MySprite = new MySprite();
someHolder.addChild(mySprite);
Now, if at some later point you remove mySprite, it'll still hang around in memory, because it has added itself (or a reference to itself) to the stage in the init() method.
In this scenario, the best way to avoid this could be removing the listener added to the stage when mySprite is removed from the display list.
private function init(e:Event = null):void {
addEventListener(Event.REMOVED_FROM_STAGE,cleanUp);
stage.addEventListener(Event.RESIZE,handleStageResize);
}
private function cleanUp(e:Event):void {
stage.removeEventListener(Event.RESIZE,handleStageResize);
}
I'm sure other people will tell you to use weak references when adding the listener to the stage, but you should remove your listeners anyway. If you don't, when you remove mySprite from the display list and have no other refs to it, will be eligible for GC and will eventually be wiped away from memory. But until that happens, the code in handleStageResize() will continue to execute.
I'll just follow up #Juan's answer - GC needs to be considered from the ground up as a critical aspect of application design. If you create an object, you must be aware of each reference to it, and remove each reference and nullify it to flag properly#. If you reference that object in array, that counts, if you reference it in a listener, that counts, if you reference it via a local variable, that counts too (though only during the life of the function), if its simply in the display list, that definitely counts, and on and on.
I go so far as to write my remove listener statements prior to adding them just to make sure.
I will almost always write a public destroy() method for any object to handle inner object hierarchies (parent calls destroy on child, which, in turn calls destroy on any children etc etc). Just removing / nulling a parent without doing so to each child is poor GC management.
And if you actually have any concerns that mem leak has sprung, trace out System.totalMemory just to make sure:
var mem:String = Number( System.totalMemory / 1024 / 1024 ).toFixed( 2 ) + ‘Mb’;
trace( mem ); // eg traces “24.94Mb”
Mostly - just be methodical about it - its not rocket science, but you have to be careful.
Cheers -
# and even if you do, flash makes up its own mind about when to actually do a sweep. The best we can to is ensure an object is properly flagged and trust that it will be dealt with efficiently.
I'm not going to post an example of this but I'll explain it a bit. There are 2 situations you are describing here.
memory leaks
processor overflows
AS3 handles memory and processor operations differently.
Memory Leaks happen when there are many objects created and destroyed. The objects leak memory when they have references and the object is destroyed with out destroying the references - therefor leaving a memory block of an unused object = leak.
Processor Overflows happen when you have many methods referencing each other with out 'closing the loop'.
Related
I want to nullify the event.target for garbage collection as the moveclip is no longer needed. What I have roughly is this:
mc.addEventListener(MouseEvent.CLICK, destroy);
public function destroy(event:MouseEvent):void {
event.target.parent.removeChild(event.target);
event.target.removeEventListener(MouseEvent.CLICK, destroyShape);
event.target = null; //THIS IS WHAT I WANT TO ACHIEVE
}
I'm sure this is relatively simple but I'm not sure how to do it.
thanks
You can't change MouseEvent.target value. It's a read only property. If your MovieClip doesn't exist (removeChild) and you removed event handler
mc.removeEventListener(MouseEvent.CLICK, destroy);
then the garbage collector automatically will remove it.
What you are trying to achieve (if it was even possible) would achieve nothing in term of Garbage Collection.
Unless redispatched the event will be garbage collected as soon as the method is done running. All properties of the event will also be discarded. This garbage collection of the event itself and its properties will have absolutely NO EFFECT on the object they point to in term of Garbage collection.
In the scope at which the event was dispatched in the first place the object will continue to exist after the event itself has been discarded. That is at that scope that the object reference must be nullified, not at the event listener scope where it will have no effect since the object still exists at the dispatching scope.
The solution accepted also does nothing. It is as relevant as applying a bandage to a wooden leg. Any local variable in function/method qualify for GC immediately after the method runs. Nullifying those variables has no effect whatsoever and does not constitute a valid answer to any problem and certainly not a GC problem. That those variables are GC does also not constitute a guaranty that the object they point to will be GC.
This is a case where a question about an inexistent and misunderstood problem is asked and answered by posting a false and irrelevant solution.
Case in point: Only a DisplayObject currently in a display list and attached to the stage has the ability to be the target of a MouseEvent. It is simply impossible for that object to become available for garbage collection before it is removed from the display list it belongs to. For those reasons that object cannot qualify for GC at the time the MouseEvent listener runs since that object still has at least one strong reference because it is attached to a display list. This is the proof that what the PO asks is misguided and any code examples are misguided as well since they cannot qualify the object for GC at that point in time.
As #subdan states, the target property of any Event is a readonly property.
You can still null your movie clip but not like in your sample.
mc.addEventListener(MouseEvent.CLICK, destroy);
public function destroy(event:MouseEvent):void
{
var myMC:MovieClip = event.target as MovieClip;
if( myMC )
{
myMC.removeEventListener(MouseEvent.CLICK, destroyShape);
myMC.parent.removeChild(myMC);
myMC = null; //THIS IS WHAT I WANT TO ACHIEVE
}
}
Hey everyone so I was wondering if recreating a variable movie clip inside a timer object every time it's called could decrease performance or cause a MEMORY LEAK. This is how I have it set up right now:
private function addSwimPowerUp(e:TimerEvent):void
{
var newSwimPower:mcSwimPower = new mcSwimPower();
stage.addChild(newSwimPower);
aSwimPowerUpArray.push(newSwimPower);
}
I do that with all my timer objects in my game. Should i just declare the var newSwimPower a private variable?
Creating a new instance of something and then not doing anything with it will result in its eventual garbage collection. That is, if your code was this:
private function addSwimPowerUp(e:TimerEvent):void
{
var newSwimPower:mcSwimPower = new mcSwimPower();
}
Then all of those new MovieClips will get cleant up.
With that said, you're adding those objects to the display tree as well asn an array. If you do not remove the object from both of those things, it will not get garbage collected and that will be a memory leak.
Creating a class level variable for newSwimPower and assigning to that instead won't make any difference, other than that if you don't garbage collect the class referencing it then the last instance you created will still be in memory.
Are there any general guide lines for using retain and release for objects in cocos2d-X ? When creating objects in a function, is it true that the functions memory is cleaned up the second the function returns. When a object is created, calling the retain function of the object, will retain object beyond the function return ?
Kind Regards
Generally in c++ you have this behaviour:
void foo() {
Object a;
Object *pA = new Object();
(…)
}
This would result in a being destroyed automatically at function end, as it was allocated on stack. The *pA would not get destroyed, as it was allocated on the heap (thus, you only loose the reference to it, but the object itself still lives).
Cocos implements a thing called "Automatic Reference Counting" : each CCObject has a reference counter and two methods retain() and release(). The way this works is, that every time you create an object, it gets registered in cocos structers (CCPoolManager). Then with every frame (between them being drawn) there is a maintenance loop which checks the reference counter of all objects : if it is 0 this means (to cocos) that no other objects reference it, so it is safe to delete it. The retain count of an object is automatically incresead when you use this object as an argument for an addChild function.
Example :
void cocosFoo() {
CCSprite *a = CCSprite::create(…);
CCSprite *b = CCSprite::create(…);
this->addChild(b);
}
What happens here is this :
Two CCSprites are created, cocos knows about them.
The b sprite is added to this object (say a CCLayer)
The function ends, no objects are destroyed (both of them being on heap).
Somewhere between this and next frame, the maintanance gets run. Cocos chcecks both sprites and sees that a has reference count == 0, so it deletes it.
This system is quite good, as you don't need to worry about memory management. If you want to create a CCSprite (for example), but not add it as a child yet, you can call retain() on it, which will raise its reference counter, saving it from automatic deletion. But then you'd have to remember about calling release() on it (for example, when adding it as a child).
The general things you have to remeber about are :
Each call to retain() by you needs to be paired with release().
You generally shouldn't delete CCObjects yourself. If you feel that you need to, there is a conveniece macro : CC_SAFE_DELETE(object)
So to answer your questions in short :
Are there any general guide lines for using retain and release for objects in cocos2d-X ?
Yes, you should generally not need to do it.
When creating objects in a function, is it true that the functions memory is cleaned up the second the function returns.
Answer to this is the whole text above.
When a object is created, calling the retain function of the object, will retain object beyond the function return ?
Yes, as will adding it as a child to another (retained in any way) object.
Here is the thing,
cocos2dx has an autorelease pool which drains the objects which have retain count=0 which is a variable to keep in check the scope of the cocos2dx object.
Now when you create new object using the create method it is already added to the autorelease pool and you don't need to release it or delete it anywhere , its like garbage collector in java, takes care of garbage objects behind your back.
But when you create new object using 'new' you definitely need to release it in its destructor or after its use is over.
Second thing,
when your object is added to the autorelease pool but you need it somewhere else you could just retain it , this increments its retain count by one and then you have to manually release it after its use is over.
Third Thing,
Whenever you add child your object it is retained automatically but you don't need to release it rather you remove it from the parent.
This question already has answers here:
disable GC in AS3
(2 answers)
Closed 9 years ago.
so, I am making really enormous project with as3, and I`d like to know how to disable garbage collector, as user might be on for long and I dont want him or her to just be cut off because garbage collector removed some listener somewhere that should be now needed.
is there easier way to disable garbage collector than go to every single listener and add "false, 0, true" extension after naming the listener?
No it's not possible, but what you're afraid of isn't really an issue.
Also, making event listeners weak doesn't disable the garbage collector, in fact it makes whatever object they are set to eligible for garbage collection if that is the only reference left to them.
That is possible actually, but it needs discipline...
When an as3 object looses all its references to others, it becomes a candidate for garbage collection. To avoid this you can use the following method:
When you need an object to persist in memory, bind it to somewhere accessible.
Here I supplied an example code.
First create the class below:
package your.package.path;
public class noGc {
protected static var vault:Array = [];
// In case we forget that noGc should stay static....
public function noGc(){throw new Exception('Error: Instance from STATIC noGc');}
public static function hold(o: *): void {
if(vault.indexOf(o)==-1)vault.push(o); // no multiple references in vault
}
public static function release(o: *): Boolean {
var i: int = vault.indexOf(o);
if(i == -1)return(false); // return value is for information only
vault.splice(i,1); // remove object from vault
return(true);
}
public static function releaseAll(): void {
vault.length = 0;
}
} // end class noGc
To avoid gc on "yourObject"
noGc.hold(yourObject);
To allow gc on "yourObject" back
noGc.release(yourObject);
For normal code flow start holding the objects right after creation of them.
Then you should release them at the end of their use.
Also you have to keep an eye on exceptions since exceptions break the normal flow you should handle them and release the objects becoming irrelevant after the exception.
Forgetting an object held means use of unnecessary memory, a.k.a. a memory leak.
As I told before, It needs discipline.
Finally, when you need to wipe all objects that are held use,
noGc.releaseAll();
Hope this helps.
I have a class which is called a number of times. When the application goes to the next stage these all have to be unloaded. Because of that, I created an unload() method in the class.
The problem is that I can't seem to set my uint variable "charId" to null in order to "unset" it. The "delete" command is not possible either as that is only applicable for dynamic variables or something in that kind of way.
Now I wonder, how am I supposed to unset this variable, so it's memory will be re-allocated later on?
The class's unload method:
public function unload():void
{
trace("Unloading character with charname '" + charName + "'.");
enterButton.removeEventListener(MouseEvent.CLICK, enterClicked);
removeChild(enterButton);
enterButton = null;
charName = null;
charId = null; //this is possible but not recommended - what's a better way?
lobbyInterface = null;
}
So yeah, it's practically possible as it changes the variable type - however it's not recommended and raising a warning. So, what's a better way to do it?
Note that this object is also unloaded in it's parent. Does that also free all these variables from memory?
uint, int, Number and Boolean are not nullable in AS3. Number can be NaN, but that is really the best you can get. int and uint are always just 32 bit, so you can't stuff a null-reference in there.
The type of cleanup you are trying to do cannot be accomplished since AS3 has the concept of sealed classes. A sealed class has a fixed size in memory. When it comes to instance variables, think of it as a C struct, you can only dump all of it, or nothing. You can do anything in C of course, it's a fixed block in memory, an entity of one reference per variable.
What you want to do is only work with dynamic variables, which are maintained differently.
You don't need to do this sort of cleanup since Flash has garbage collection like most runtimes nowadays. It also deals with nested and circular references, the only thing you have to be sure about is, that you delete any "outer" references to that class. Things that are generally not collected are objects on the display list, running timers and intervals, and I/O related stuff. As soon as you have a reference chain from there to your object, it will not be collected.
Let us say you have an object A with an event handler for a mouse movement on an object on some list, referencing an object B. B will not be collected, but as soon as there is no chain leading to an object, it will be collected (sooner or later, the GC is quite lazy. But the more memory you use, the more it does its work).