March 28th, 2007 by Kyle
Posted in: Flex, Flash Player, actionscript
I have been asked about this from various different angles and responded to it (often with ammo from others) in various different ways. I have decided to compile all info I can as a starting point to address questions hat typically come up from Flex customers regarding Flash Player Memory Management and Flash Player Garbage Collection.
CAVEAT: I am not a Player engineer and haven’t looked at the Flash Player code, but this is my understanding from several discussions with the engineers on the Flash Player and Flex Teams. (Some of this info is taken directly from emails, forum postings and conversations I have had with Alex Harui, Matt Chotin and Gordon Smith.)
Also, Grant Skinner goes into far more detail on his blog about Flash Player Memory management and Garbage Collection.
If you want to know more than the high level that I am going to address here you should check out his blog entries:
http://www.gskinner.com/blog/archives/2006/09/resource_manage.html
http://www.gskinner.com/blog/archives/2006/06/as3_resource_ma.html
http://www.gskinner.com/blog/archives/2006/09/garbage_collect.html
The underlying Flash player is essentially a garbage collection memory model. That means that if nobody has a reference to an object, it will be collected and thrown out eventually. Thus, you never have to delete anything, just make sure all of your references to it are broken by removing objects from the displaylist, removing event listeners or using weak listeners where appropriate.
The Flash Player runs within the browser’s memory space and Firefox or IE give memory to the player. The browsers are then responsible for the memory and will release it to the heap.
Here is a general description of what is generally going onwith memory management as an app runs:
The Flash Player grabs OS memory in large chunks, divides them up into smaller chunks and allocates those small chunks to the application. As the number of small chunks dwindles, the player runs a GC pass to see
if any of those small chunks can be freed before we go to the OS for another big chunk. If the application is idle and nothing is accessing memory, nothing is going to force a GC pass so nothing will ever get freed.
So lets say the Flash Player allocated 40 megs. If the app happened to get to 33 megs there may be no need for the Player to GC. And it could be that in reality the Player could get away with only 20 megs, but it will take that whole 40 b/c the OS is letting it. Still not technically a memory leak, even if you think the Windows Task Manager should show less.
The MemoryMonitor tries to force a GC pass (see the hack comment in the code). It is unclear as to whether the hack works perfectly.
GC is opportunistic. This means it does not run all of the time. It tends to be triggered by allocating memory instead of freeing it, so watching an idle app will almost never result in GC. Because of the fact that GC is more or less “opportunistic”, there is no current way to manually test for memory leaks. I think there are situations where lots of activity forces several large chunks to be acquired from the OS and then, when most of that stuff is freed, each of the large chunks has a few small chunks still in use and so the OS chunks are not released back to the OS because there isn’t enough activity to cause a GC pass. Therefore, the only way we “prove” there is a memory leak issue is to use MemoryMonitor’s callback to repeat a sequence over and over again.
Now, lets look at an example.
In this example, the MemoryMonitor component is a slight extension of code mentioned in the docs here:
http://livedocs.macromedia.com/flex/2/docs/00001383.html
Here is the MemoryMonitor component:
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
private var timer:Timer;
private var maxmem:Number = 0;
private var minmem:Number = 0;
private var lasttotal:Number = 0;
import mx.controls.Alert;
import mx.managers.PopUpManager;
// vars used to iteratively popUp and close Alerts
private var count:int=0;
private var alrt:Alert;
[Bindable]
public var maxIterations:int=100;
[Bindable]
public var forceGC:Boolean = true;
public function gcHack():void{
// unsupported hack that seems to force a full GC
try
{
var lc1:LocalConnection = new LocalConnection();
var lc2:LocalConnection = new LocalConnection();
lc1.connect(‘name’);
lc2.connect(‘name’);
}
catch (e:Error)
{
}
}
private function timerHandler(event:Event):void
{
if(forceGC){gcHack();}
var totalmem:Number = System.totalMemory;
if (totalmem < lasttotal)
trace("collected at", maxmem);
lasttotal = totalmem;
total.text = (totalmem/1024).toString() + " K";
maxmem = Math.max(maxmem, System.totalMemory);
max.text = (maxmem/1024).toString() + " K";
if(minmem==0){
minmem=totalmem;
min.text=(minmem/1024).toString() + " K";
}
else{
minmem=Math.min(minmem,System.totalMemory)
min.text=(minmem/1024).toString() + " K";
}
/*
Start of code to iteratively popup and close Alerts
*/
if (callbackFunction != null){
alrt=Alert.show(count.toString());
}
else{
PopUpManager.removePopUp(alrt);
if(count<=maxIterations){
alrt=Alert.show(count.toString());
count++
}
else{
stop();
count=0;
}
}
/*
end of code to iteratively popup Alerts
*/
if (callbackFunction != null)
callbackFunction();
}
public var interval:int = 200;
public var callbackFunction:Function = null;
public function start():void
{
timer = new Timer(interval);
timer.addEventListener("timer", timerHandler);
timer.start();
}
public function stop():void
{
timer.stop();
timer.removeEventListener("timer", timerHandler);
timer = null;
}
]]>
</mx:Script>
<mx:Label text="min memory:" />
<mx:Label id="min" text="0" width="100" />
<mx:Spacer width="10" />
<mx:Label text="current memory:" />
<mx:Label id="total" text="0" width="100" />
<mx:Spacer width="10" />
<mx:Label text="max memory:" />
<mx:Label id="max" text="0" width="100" />
</mx:HBox>
If you look at the sample code provided there is an Alert on the first callback and on the second callback, the Alert is removed. This is repeated a specified number of times (in my example by the numeric stepper.) The memory usage should grow and then reach a stable state. If it doesn’t there is a leak.
Because of the large chunk mechanism, it can make it look like the player eats tons of memory because there are pages allocated for small chunks, medium chunks, large chunks etc. But that is usually as the app “gets going” and it should settle over time which is why we use MemoryMonitor to repeat a sequence hundreds of times. Since the large chunk mechanism is a layer of abstraction between allocation of actionscript objects and OS memory, freeing something in actionscript is not guaranteed to have an effect on OS memory.
Here is the simple app:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*">
<MemoryMonitor id="mm" maxIterations="{ns.value}" />
<mx:NumericStepper id="ns" minimum="1" maximum="500"
stepSize="10"
value="1"
width="65"/>
<mx:Button label="open Alerts" click="mm.start()"/>
</mx:Application>
Now lets run the sample and see what I described above:
Compile the app (in Flex Builder), main.mxml, and run it in Firefox (or IE).
In the Windows Task Manager firefox.exe is at 29,600 K.
If I run 1 Alert
Firefox.exe = 35,220 K
Min memory = 5428 K
Current memory = 8096 K
max memory = 10168 K
I open 50 Alerts.
Firefox.exe varies between about 44,000 and 37,000 K as alerts open and close and eventually when the test is over settles down to around 35,600 K
Min memory = 5428 K
Current memory = 8348 K
max memory = 10384 K
So basically GC did its thing (the fact that Current is less than Max means that something must have been GCd) and some of the memory that the FlashPlayer grabbed during my scenario is being allocated back to the OS and some is not as per the FlashPlayer’s memory allocation scheme.
Feel free to run some more tests and play with this app.
And here is a link to a Flex Builder 2.0.1 project containing all the source.
And just before I got a chance to finish this up and post, Alex Harui beat me to the punch and just posted a very good powerpoint and sample on his blog regarding this topic:
http://blogs.adobe.com/aharui/2007/03/garbage_collection_and_memory.html
(Some of my content looks a lot like his…he has shared his explanations with me a few times and his content has made its way into my explanations.)





Recent Comments