I'm developing an AS3 application which has some memory leaks I can't find, so I'd like to ask some newbie questions about memory management.
Imagine I have a class named BaseClass, and some classes that extend this one, such as ClassA, ClassB, etc.
I declare a variable:
myBaseClass:BaseClass = new ClassA();
After a while, I use it to instantiate a new object:
myBaseClass = new ClassB();
some time after
myBaseClass = new ClassC();
and the same thing keeps happening every x millis, triggered by a timer.
Is there any memory problem here? Are the unused instances correctly deleted by the garbage collector?
Thanks!
Assuming you have no other references to the instance (or, possibly, its contents), the garbage collector will clean them up. However, the time before cleanup is, as far as I know, indeterminate (there might be some hard timeline in use, but I've never seen it documented). If you're creating a huge number of instances, you might use up a lot of memory before the first ever gets cleaned up.
There is an AS call (the name of which escapes me at the moment) to force a GC run, but it shouldn't normally be necessary. If you find it necessary, you almost certainly need to rethink how your application works.
If myBaseClass is the only stored reference to
new ClassA()
and you assign something else to myBaseClass
new ClassB()
then there will be no reference pointing to ClassA and the garbage collector should find it when it runs.
However, if you give myBaseClass to a library or class that stores its own reference to that object, when you reassign myBaseClass there will still be a reference pointing to the ClassA and the garbage collector will not clean it up, creating a memory leak. Normally a well written library will provide you with a way to remove the reference. e.g.
var child:Sprite = new Sprite()
// one reference to the new Sprite
stage.addChild(child); // assume stage stores reference
// two references to the new Sprite
/**
* assume the following:
*
* child = null;
*
* one reference would still remain in stage
* garbage collector will not clean up the sprite
*/
stage.removeChild(child); // assume stage clears reference
// one reference left
child = null;
// no reference to the sprite, garbage collector will clean it up
Hope this clears things up a bit. Essentially you want to keep track of how many references there are to an object, if you suspect it to be leaking memory.
I know that FlashDevelop has a profiler that is quite useful for finding these bugs. Also, you need to know that when the garbage collector runs... it will free up memory and give a saw tooth pattern to your memory profiler.
Good example code for demonstrating profiling in Actionscript 3
AS3 Memory Leak Example
Related
We have a huge flex applications using multiple modules. There is a huge memory leak problem over prolonged usage of loading and unloading modules.
Based on all the search and different articles I understand that I need to clean up objects on unload, remove event listeners, stop timers and dispose any references.
I started this by picking up one component at a time within one of the module.
Here is how this is structured.
There is one parent application which loads a module, which has multiple views. The component is defined in mxml and is referenced in the mxml module in a view stack.
This mxml component is a VBox with event listeners added as-
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"
paddingTop="10"
paddingLeft="10"
paddingBottom="10"
paddingRight="10"
creationComplete="onInit()"
show="onShow()"
resize="onResize(event)" ....
There are couple of properties that are binded from the parent container in mxml. Other than the above listeners there is also a private variable accessed from outside -
[Bindable]
private var _model:SModelLocator=SModelLocator.GetInstance();
On unload of the module I call a dispose function in this component as below-
public function dispose():void
{
this.removeEventListener(FlexEvent.CREATION_COMPLETE,onInit);
this.removeEventListener(FlexEvent.SHOW,onShow);
this.removeEventListener(ResizeEvent.RESIZE,onResize);
var arr:Array = this.getChildren();
for(var i:int = 0; i<arr.length;i++)
delete arr[i];
this.removeAllChildren();
_model = null;
//Properties that are binded from the parent container
Property1 = null;
Property2 = null;
this.deleteReferenceOnParentDocument(this.parentDocument as IFlexDisplayObject);
}
Now when I run profiler and switch between modules number of instances of this component still continues to grow. I clicked on GC Collect on profiler and still the instances stay.
On the parent container which is the module mxml, I also tried writing the following lines upon unload of module-
//function call to invoke dispose as above
component1.dispose();
component1 = null;
Please help. I am not sure what am I missing here, or even if this is the right approach.
Thanks.
This will not solve your problem but I hope it helps.
First and foremost you are not getting anywhere just by looking and refactoring code. You need hardcore data that proves that you have a leak, which then will tell you what is leaking so you can fix it. From all the memory profilers I've used the FlashBuilder one is still the best, the IntelliJ one was not reliable a year and Adobe Scout only does performance analysis.
Start by your smallest modules and with the memory profiler open prove that opening and closing a module (preferably in isolation of the main rig) creates a leak. If that's the case I would start by removing ALL the code from the module, and testing it again, add part by part which will lead you to the lead eventually. You can use a best suspects search, where you first address event listeners, etc.
This article from Thomas Sugden is still the best I've read about flex memory profiling and you should read it from end to end if you haven't.
It worth your time to write tools that allow you to test your modules, who knows even automate the process that evaluates if there are leaks or not. This is important because sometimes there are leaks that will not be your fault, the Flex framework has a bunch of them that you just can't avoid.
Hope this helped.
You might want to try a different container. I've personally had performance issues with VBox. And as the pervious user said, Flex has a habit of waiting until memory reaches high levels before it performs a memory sweep.
Flash doesn't always initiate the memory sweeping method, but it only frees null pointers when you are taking up memory excessively, so do mind that hindrance.
Tried finding some info on this without success.
When instantiating a movieclip for later use. Is it using a lot of performance before actually adding it to stage? The question is when is flash working more. At adding to stage or instantiating to mmemory?
It depends on what is in it and what it is doing. If everything in it is graphic, it should only use CPU if it is on the Display list. If it has ActionScript code, it will depend on what that code is doing. For example, listening for ENTER_FRAME will use significant CPU cycles, as will instantiating new objects. Note that having objects coming and going from the timeline also instantiates objects.
If the object contains a lot of data (for example a BitmapData that is a 1000 x 1000 uncompressed jpg), then it will use a lot of memory.
Is flash smart enough to "hide" PIXELS that aren't on the stage, in order to decrease memory usage? Or I must do it manually, if it decreases the memory usage at all?
Flash does not render objects that aren't on the stage (as per http://help.adobe.com/en_US/as3/dev/WS5b3ccc516d4fbf351e63e3d118a9b90204-7e3e.html).
However I think you may be mixing up two different issues.
One issue is CPU/GPU performance - for this there is no need to worry about off-stage objects as Flash does not waste time rendering display objects that are outside the stage bounds.
The other issue is memory usage. Every object that you create takes up some memory whether or not it is visible on the screen. Flash has a garbage collector that will periodically dispose of unused objects, however "unused objects" means an object that isn't referenced by any other object so if you are having memory issues you will have to manually clean up objects by removing event listeners, nulling references etc.
There's nothing like that available to decrease memory usage. If it's visible on your monitor, it needs to be rendered by Flash and have a place in memory storing the pixel colour.
Although Flash is very fast these days, especially with hardware acceleration. So you shouldn't worry too much about performance, there's a lot of bang for your virtual buck with AS3. I'd bet all of my virtual dollars on it.
Flash will store all bitmaps, movieclips in fact all objects in memory as expected. If you have a large bitmap which is larger than the stage, it still occupies memory regardless of you only showing a portion of it.
If you have multiple bitmaps or movieclips that may move off the stage and no part of them are visible, then the only way to recover memory is to make sure the object is dereferenced and set to null.
myMovieClip = null;
Prior to setting to null you would also have to make sure that nothing else is referencing the object, for example it can't be stored in an array or have any event listeners attached to it so therefore:
myMovieClip.removeEventListener(Event.WHATEVER, eventHandler);
For bitmapdata objects you would need to call dispose first before setting to null:
myBitmapdata.dispose();
myBitmapdata = null;
This then allows the GC to recover memory when it chooses, unless you are using AIR which means you may request a gc call yourself:
System.gc();
If you are developing in Flash Builder, the best practice is to regularly profile your application and hit the button to force a gc call. You can then see which objects are persisting in memory and locate the references which are causing the memory leaks.
I do this a lot to configure new instances of classes
var myVar = new MyClass({param:1,param2:true,param3:"hello"});
Does this anonymous object gets garbage collected?
Would it be better to create and destroy the object after using it?
var myConfig:Object = {param:1,param2:true,param3:"hello"}
var myVar = new MyClass(myConfig);
myConfig = null;
I believe you are referring to "object literals", which should be garbage collected.
Flash uses two approaches to garbage collection - reference counting and mark and sweep.
If not caught by reference count, it should ultimately be released by mark and sweep.
Reference Counting
Each object on the heap keeps track of the number of things pointing to it. Each time you create a reference to an object, the
object's reference count is incremented. When you delete a reference,
the object's reference count is decremented. If the object has a zero
reference count (nothing is pointing to it), it is added to the Zero
Count Table (ZCT). When the ZCT is full, the stack is scanned to find
any references from the stack to an object on the ZCT. Any object on
the ZCT without a stack reference is deleted.
One of the problems of deferred reference counting is circular references. If ObjectA and ObjectB refer to each other but no other
objects in the system point to them, they will never have a zero
reference count and will therefore never be eligible for garbage
collection using reference counting. This is where mark and sweep
garbage collection helps.
Mark/Sweep
Applications that run in Flash Player or AIR have multiple GCRoots.
You can think about a GCRoot as the trunk of a tree with the objects
of the application as the branches. The Stage is a GCRoot. Loaders are
GCRoots. Certain menus are GCRoots. Every object that is still in use
by the application is reachable from one of the GCRoots within the
application. GCRoots are never garbage collected.
Every object in an application has a "mark bit." When the Mark phase
of garbage collection begins, all of those mark bits are cleared. The
MMgc keeps track of all GCRoots in the application. The garbage
collector starts from those roots, traces through each object and sets
the mark bit for every object it reaches. Any object that is no longer
reachable from any of the roots is no longer reachable from anywhere
in the application – its mark bit does not get set during the Mark
phase. Once the collector is done marking all of the objects it finds,
the Sweep phase begins. Any object that doesn't have a set mark bit is
destroyed and its memory reclaimed.
apologies if this is a dupe; i couldn't find it.
i've read and understood grant skinner's blog on the AS3 garbage collector -
http://www.adobe.ca/devnet/flashplayer/articles/garbage_collection.html ,
but my question isn't covered there.
here's my question.
suppose i've written some AS3 code like:
statementOne;
statementTwo;
is there any possibility that the garbage collector will run during or between my two statements, or does it only run after my "user" code has finished and returned control up to flash ?
we have an A-Star codeblock that's sometimes slow,
and i'd like to eliminate the GC as a potential culprit.
the codeblock is obviously more complex than my example above,
but it doesn't involve any events or other asynchronous stuff.
tia,
Orion
The garbage collector isn't threaded, so generally speaking it won't run while your code is running. There is one exceptional case where this isn't true, however.
The new keyword will invoke the garbage collector if it runs out of memory.
You can run this code to observe this behaviour:
var vec:Vector.<*> = new Vector.<*>(9001);
for (;;) {
vec[0] = new Vector.<*>(9001);
vec = vec[0];
}
Memory usage will quickly jump up to the maximum (1GB for me) then hold until the time cutoff.
There are many good answers here but I think a few subtleties have not been addressed.
The Flash player implements two kinds of garbage collection. One is reference counting, where Flash keeps a count of the incoming references to each object, and knows that when the count reaches zero the object can be freed. The second method is mark-sweep, where Flash occasionally searches for and deletes isolated groups of objects (where the objects refer to one another, but the group as a whole has no incoming references).
As a matter of specification either kind of garbage collection may occur at any time. The player makes no guarantee of when objects will be freed, and mark-sweeps may even be spread out over several frames. Further, there's no guarantee that the timing of GCs will stay the same between Flash player versions, or platforms, or even browsers. So in a way, it's not very useful to know anything about how GCs are triggered, as you have no way of knowing how widely applicable your knowledge is.
Previous point aside, generally speaking mark-sweeps are infrequent, and only triggered in certain circumstances. I know of certain player/OS configurations where a mark/sweep could be triggered every N seconds, or whenever the player exceeded P% of the total available memory, but this was a long time ago and the details will have changed. Reference-counting GCs, in contrast, can happen frequently - between frames, at least. I don't think I've seen them demonstrated to happen more frequently, but that doesn't mean it can't occur (at least in certain situations).
Finally, a word about your specific issue: in my experience it's very unlikely that the GC has anything to do with your performance problem. Reference-counted GC processing may well occur during your loops, but generally this is a good thing - this kind of collection is extremely fast, and keeps your memory usage manageable. The whole point of managing your references carefully is to ensure that this kind of GC happens in a timely manner.
On the other hand, if a mark/sweep occurs during your loops, that would certainly affect your performance. But this is relatively hard to mistake and easy to test. If you're testing on a PC and your application doesn't regularly climb into the hundreds of MBs of memory usage (test with System.totalMemory), you're probably not getting any mark/sweeps at all. If you do have them, it should be easy to verify that the associated pauses are large, infrequent, and always accompanied by a big drop in memory usage. If those things aren't true, you can disregard the idea that GC is part of your problems.
To my knowledge, this is not documented. I have a feeling the GC won't run while your code is being executed (that is, while your code is on the execution stack; each frame, the player creates a new stack for use code). Obviously, this comes from observation and my own experience with Flash, so I wouldn't say this is 100% accurate. Hopefully, it's an educated guess.
Here's a simple test that seems to show that the above is true:
package {
import flash.display.Sprite;
import flash.net.FileReference;
import flash.system.System;
import flash.utils.Dictionary;
import flash.utils.setTimeout;
public class test extends Sprite
{
private var _dict:Dictionary = new Dictionary(true);
public function test()
{
testGC();
setTimeout(function():void {
traceCount();
},2000);
}
private function testGC():void {
var fileRef:FileReference;
for(var i:int = 0; i < 100; i++) {
fileRef = new FileReference();
_dict[fileRef] = true;
traceCount();
System.gc();
}
}
private function traceCount():void {
var count:int = 0;
for(var i:* in _dict) {
count++;
}
trace(count);
}
}
}
The GC seems to be particularly greedy when there are FileReference objects involved (again, this is from my experience; this isn't documented as far as I know).
Now, if you run the above code, even calling explicitly System.gc(), the objects are not collected while your function is on the stack: you can see they're still alive looking at the count of the dictionary (which is set to use weak references for obvious reasons).
When this count is traced again, in a different execution stack (caused by the asynchronous call to setTimeout), all objects have been freed.
So, I'd say it seems the GC is not the culprit of the poor performance in your case. Again, this is a mere observation and the fact that the GC didn't run while executing user code in this test doesn't mean it never will. Likely, it won't, but since this isn't documented, there's no way to know for sure, I'm afraid. I hope this helps.
GC will not run between two statements that are on the same stack / same frame like you have in your example. Memory will be freed prior to the execution of the next frame. This is how most modern VM environments work.
You can't determine when GC will run. If you are trying to stop something from being GC'd, keep a reference to it somewhere.
According to this Tom from Gabob, large enough orphaned hierarchies do not get garbage collected.