On a recent project, we had a performance problem. The AIR project’s requirements include the heavy adding/removing of UI elements and over time we could easily see the performance slowed. We knew there was a something wrong. It was a memory leak. A memory leak starts when an object is stored in memory but cannot be accessed by the running code. When the undesired object cannot be freed from memory, and is not usable, it is a leak. Over time the application may leak (or re-leak the same issue) more and more. Eventually the application may show signs of the leak or even become unusable.
User Experience Symptoms Of A Memory Leak
- During regular use the application becomes more and more sluggish/slow. If there is nothing ‘new happening’ onscreen and no heavy ‘rendering’ it is more obvious to notice the Framerate-per-second (FPS) lower. You can use a small debug window (such as Hi-Res-Stats formerly MrDoobs Stats) to show the current FPS and estimated ram usage to help you notice this. If you see the FPS run at 30 for example during the first minute of use and 20 after 5 minutes, there may be a memory leak.
- The application quits suddenly. This could be for many reasons, but it may be that the application runs out of memory.
- Flash throws the memory-specific error ‘flash.errors.MemoryError’
Profiling To Find Evidence Of A Memory Leak
- Run your application with the Flash Builder Profiler. Run -> Profile As… -> etc…
- Watch the ‘Memory Usage’ Panel
- Look at the curves of Peak Memory (Red) and Current Memory (Blue). The analysis is totally application dependent. In your particular application, if you expect memory not to grow, but you see it grow, that is a problem. If you expect the memory to drop (UI removed from stage, arrays and vectors emptied, etc…) and you don’t see it drop, that is a problem. Herein lies the art of memory profiling. Consider to add a button to ‘Reset Application’, then click it and see that indeed the Current Memory drops to zero (0).
- Watch the ‘Live Objects’ panel. Compare the 1. ‘Cumulative Instances’ and 2. ‘Instances’. For each object. #1 shows the total objects every created since the application started and #2 shows only those currently in memory. If these numbers are the same, and should not be, that is a problem. Perhaps you feel you have deleted a sprite from the stage or deleted another object from memory yet it still exists.
Tools And Tips For Finding And Fixing Memory Leaks (Must Read!)
- Memory Tracker – http://divillysausages.com/blog/tracking_memory_leaks_in_as3.
- Solving Memory Leaks (FANTASTIC!) – http://www.tikalk.com/flex/solving-memory-leaks-using-flash-builder-4-profiler
Example 1: Without Memory Leak
[actionscript3]
package
{
import flash.display.Sprite;
import flash.events.Event;
//USE A LOW FRAMERATE, SO WE CAN STUDY CLOSELY
[SWF(frameRate="1")]
public class MemoryLeakDemo extends Sprite
{
private var listOfDots_vector:Vector.<CustomDot>;
public function MemoryLeakDemo()
{
//REPEAT SOME CODE EVERY SECOND
addEventListener(Event.ENTER_FRAME, _onEnterFrame);
//CREATE A LIST
listOfDots_vector = new Vector.<CustomDot>();
}
protected function _onEnterFrame(event:Event):void
{
//EVERY FRAME WE…
//1. CREATE A NEW ‘DOT’ (A Red Circle Sprite)
//MEMORY NOTE: ‘var’ is a temporary variable.
//So CustomDot has 0 (permanent) references
var customDot : CustomDot = new CustomDot();
//2. ADD TO THE STAGE
//MEMORY NOTE: So CustomDot has 1 (permanent) reference; ‘this’
addChild(customDot);
//3. REMOVE TO THE STAGE
//MEMORY NOTE: So CustomDot has 0 reference
removeChild(customDot);
//So…
//THERE IS NO LEAK
//The GC will *mark* the ‘customDot’ as having 0 references and
//The GC will *sweep* it away from memory.
}
}
}
[/actionscript3]
Example 2: With Memory Leak
[actionscript3]
package
{
import flash.display.Sprite;
import flash.events.Event;
//USE A LOW FRAMERATE, SO WE CAN STUDY CLOSELY
[SWF(frameRate="1")]
public class MemoryLeakDemo extends Sprite
{
private var listOfDots_vector:Vector.<CustomDot>;
public function MemoryLeakDemo()
{
//REPEAT SOME CODE EVERY SECOND
addEventListener(Event.ENTER_FRAME, _onEnterFrame);
//CREATE A LIST
listOfDots_vector = new Vector.<CustomDot>();
}
protected function _onEnterFrame(event:Event):void
{
//EVERY FRAME WE…
//1. CREATE A NEW ‘DOT’ (A Red Circle Sprite)
//MEMORY NOTE: ‘var’ is a temporary variable.
// So CustomDot has 0 (permanent) references
var customDot : CustomDot = new CustomDot();
//2. ADD TO THE STAGE
//MEMORY NOTE: So CustomDot has 1 (permanent) reference; ‘this’
addChild(customDot);
//2B. CREATE ANOTHER REFERENCE TO THE DOT
listOfDots_vector.push(customDot);
//3. REMOVE TO THE STAGE
//MEMORY NOTE: So CustomDot has 0 reference
removeChild(customDot);
//So…
//THERE *IS* A LEAK
//While we are correctly calling removeChild
//There is a reference left in ‘2B’ above.
}
}
}
[/actionscript3]
Next Steps
- Download the code and check it out! (See Member Resources)
- Comment below with your thoughts.
- NOTE: Flash Builder 4.7 (BETA2) has new Profiler called “Monocle“. It looks very exciting, yet this article focuses on the currently available (non-BETA) profiler.
Member Resources
[private_Free member]Enjoy this members-only content!
- Download the source-code to the MemoryLeakDemo
[/private_Free member]