Profiling Flash Applications with Flex Builder 3
The Flex Profiler is a new addition to Flex Builder 3 and is a powerful tool that enables you to watch an application as it allocates and clears memory and objects. It connects to your application with a local socket connection.
As the Profiler runs, it takes a snapshot of data every few milliseconds and records the state of the Flash Player at that snapshot, a process referred to as sampling. By parsing the data from sampling, the Profiler can show every operation in your application. The Profiler records the execution time of those operations, as well as the total memory usage of objects in the Flash Player at the time of the snapshot.
Following links provides a step-by-step information on how to start profile applications using Flex Builder 3.
1. Flex Memory Profiling
Memory profiling involves examining the memory used—as well as the memory currently in use—by objects in your application. Those objects could be simple classes, such as Strings, or complex visual objects, such as DataGrids. Using memory profiling, you can determine whether an appropriate number of objects exist and whether those objects are using an appropriate amount of memory.
Understanding Flex Memory Management and VM Garbage Collection
Flash Player Memory Allocation
Flash Player is responsible for providing memory for your Flex application at runtime. When you execute a line of code that creates a new instance of the DataGrid class, Flash Player provides a piece of memory for that instance to occupy. Flash Player in turn needs to ask your computer’s operating system for memory to use for this purpose.
The process of asking the operating system for memory is slow, so Flash Player asks for much larger blocks than it needs, and keeps the extra available for the next time the application requests more space. Additionally, Flash Player watches for memory that’s no longer in use, so that it can be reused before asking the operating system for more.
Flash Player Garbage Collection
Garbage collection is a process that reclaims memory no longer in use, so that it can be reused by the application—or, in some cases, given back to the operating system. Garbage collection happens automatically at allocation, which can be confusing to new developers. This means that garbage collection doesn’t occur when memory is no longer in use, but rather when the application asks for more memory. At that point, the process responsible for garbage collection, called the garbage collector, attempts to reclaim available memory for reallocation.
The garbage collector follows a two-part procedure to determine which portions of memory are no longer in use:
1. Reference counting
2. Mark and sweep
Following are some links which will be useful for anyone looking into Flex GC and Flash Player memory design.
1. PPT from Adobe on Memory Management and GC
2. Resource Management
3. Article on GC from Adbove Devnet
4. Sample Flex app with a straightforward memory leak
General observations on Flex GC
- GC is invoked during memory allocation and not asynchronously as a background thread in Java JVM
- IE minimize/maximize/restore operations seems to fire the GC, releasing the memory
1. Memory Leaks caused by Event Listeners
Always remove unused event listeners. Each time an event listener is added to an object, it increases the object's reference count. So the reference remains, until the event listener is removed. If for some reason, you cannot remove the event listener use the useWeakReference parameter in the addEventListener. This does not increase the reference count.
When you call addEventListener() on the TextInput instance, it responds by adding a reference to the object (the one that contains the handleTextChanged method) to a list of objects that need to be notified when this event occurs. When it's time to broadcast the change event, the TextInput instance loops through this list and notifies each object that registered as a listener. In terms of garbage collection, this means that, in certain circumstances, if an object is listening for events it may never be available for garbage collection.
The following example shows a simple case:
var textInput:TextInput = new TextInput();
When adding an event listener to a broadcaster, the developer can specify that the event listener should use weak references. This is accomplished by specifying extra parameters for the addEventListener() method:
var textInput:TextInput = new TextInput();
textInput.addEventListener('change', handleTextChanged, false, 0, true);
2. Using Item Renderer
Use item renderers judiciously. An item renderer derived from a Container class comes with lot of unnecessary overhead. Instead use a simpler class, may be an Actionscript class derived from a UIComponent. This would reduce a lot of overhead.
3. Using Images
Use images that are smaller in size and when they are large in number prefer not to embed them in the application. As far as the image formats are concerned, PNG images are much faster than other image types.
Use BitmapData as much as possible. Use dispose() method of BitmapData to free memory that is used to store the BitmapData object.
Use Binding only when necessary. Data binding expressions usually take up memory. Prefer assignments to Binding whenever possible.
Accessing local variables is much faster. If you have variables that need to be accessed more use local variables as they are stored on the stack and accessing them is much faster.
You could help the garbage collector by assigning unused variables to null.
6. Instance Creation
For components use deferred instantiation. This would immensly reduce the startup time. Be wary of creationPolicy="all". Try to avoid removeChild() / addChild() when it would work just as well to reuse an object or just toggle the visible property.
Minimize the use of containers. Try not to nest HBoxes within VBoxes and so on. Nested containers make up to huge overheads.
8. Types Conversions
Use types for the variables. Avoid implicit type conversions and when unsure of the type use the "as" operator.
Repeaters have a property called recycleChildren.Set it to true. When set to true, the repeater reuses the children it already created instead of creating new ones.
Use weak references in the Dictionary object.
Don't unnecessarily load/unload modules. If you need to unload a module, make sure to remove all references pointing to it. In particular if you have an event listener from within the module to something outside the module, that can prevent the module's memory from being reclaimed.
After making a NativeWindow you must call close() before it can be GC'ed. But you must remove references before you can call close(). Also when you open a FileStream object in asynchronous mode, pending event listeners can prevent the FileStream object from being GC'ed.
Performance profiling is used to find aspects of the flex application that are unresponsive, or where performance can be improved. When profiling for performance, generally one should be looking for methods that are executed very frequently, or methods that take a long time whenever they’re executed. The combination of those two factors usually provides a good indication of where your time should be spent in optimizing or potentially refactoring.
Using the Flex Builder 3 Profiler, one can identify the slowest portions of the application and optimize it. Flex Builder profiler allows one to take performance snapshots to record how long was spent in each function. This is useful to identify the areas of code that might benefit from optimization. While profiler is running everything is much slower. Often Mouse or similar Events will seem to take lots of time, you can ignore these. Investigate your own functions and see if they have been called too often or they take too long.
Flex Performance Tuning Tips
Flex Speed Tips: Matt chotin has shared a some great tips to improve Flex performance. Following are a few of them:
* If you have a type in AS3, which you are not sure of always use the As operator to cast the type before you use it. This avoids VM errors with try/catch, which slow the execution and is ten times slower than the As operator.
* Array is access is slow if the array is sparse. It may be faster to put nulls in empty values as this speeds things up. Array misses are very slow, up to 20 times slower than finding a valid entry.
* Avoid implicit type conversion. In the player it will convert integers to numbers and back when asked to add integers. You might as well use numbers for everything and convert back to integer at the end.
* Local variable access is faster, so assign variables to local if they are accessed a lot. They will be stored on the stack and access is much quicker.
* Data Binding expressions take up memory and can slow down the application startup. It may be more efficient to do an assignment in code rather than using binding.
* Find a slow computer and run your application. If it runs OK ship it! Other wise you can use flash.utils.getTimer():int to get a time value in miliseconds before and after some process to time it.
More information on tuning ActionScript can be found in Matt chotin's article