Tom Sugden: Modules Archives

February 22, 2010

How to Unload Modules Effectively

It's well known that unloading modules in Flex can be tricky. The principle is simple: remove all references to the module and it will become eligible for garbage collection. The harder part is that those references can exist in unexpected places. Did you know that the code generated by the Flex 3 MXML compiler when processing a CSS stylesheet can sometimes cause a module to be pinned in memory? There are a number of causes of memory leaks like this, but they can be avoided by following certain practices.

This blog post demonstrates how to unload a Flex 3 module effectively that is styled and localized. It applies several of the techniques described in Alex Harui's earlier post, What We Know About Unloading Modules. Note that Flex 4 (out now!) and Flash Player 10.1 both bring improvements in the area of memory management, such as the modular styles sub-system in Flex 4, so not all of the points below are applicable to Flex 4 projects.

Getting Started

The best way to understand module unloading is to create a sample project and run some tests. Here's a sample Flash Builder project that uses Flex SDK 3.5 to demonstrate the effective unloading of a module. A number of practices are applied and these are described later.

The sample project contains an application that loads and unloads a module repeatedly, displaying the System.totalMemory reading. Click the image below to launch the application:

ExampleApp.png

You can click the "Load/Unload" toggle button to load and unload the module manually, or the "Run Stress Test" button to begin repeatedly loading and unloading the module for a given number of repetitions. A checkbox can be used to control whether or not the garbage collector is forced after each unload. When running the application from Flash Builder in debug mode, you should see the following trace produced by Flash Player when the "Unload" button is clicked:

[Unload SWF] Workspaces:MyWorkspace:ModuleUnloading:bin-debug:module:MyModuleStyles.swf
[Unload SWF] Workspaces:MyWorkspace:ModuleUnloading:bin-debug:module:MyModule.swf

This trace indicates that the module and associated compiled CSS SWF have both been successfully unloaded. When running the stress test in the release Flash Player, you should observe the memory falling after each unload and no excessive accumulation of memory, which would indicate a module unloading memory leak. The figure below shows the expected profile when a module is not unloading effectively on the left-side, with the expected profiling when unloading is successful on the right-side:

MemoryProfile.png

You can then make alterations and observe the impact on memory consumption. If you make some changes and you no longer observe this trace, then you have pinned the module in memory, creating a memory leak! Use the memory profiling features of the Flash Builder Profiler to see which objects exist before loading and after unloading, and use your operating system tools (Task Manager, Activity Monitor, etc.) to watch the real memory footprint of the browser process while running the application.

Note that when using the Flash Builder Profiler you need to be running the debug version of Flash Player, but while observing the operating system process, you need to use the release player to get an accurate picture. It's handy to have the installers for both versions to hand, or if you're running on Windows, you might install one version in Internet Explorer and the other in Firefox. The FlashSwitcher plug-in for Firefox is another option.

The rest of this article explains the practices that have been followed in the sample application to ensure the module unloads effectively. While reading it, you can refer to the comments inside the source code that highlight each practice.

Practices for Unloading Modules

Take Care With Style Tags

When the MXML compiler processes a Style tag, it generates some code for registering the specified style selectors with the StyleManager. This code produces a back-reference from the 'StyleManager' to the auto-generated class that performs the registration. If a Style element is used inside an application that is no problem, but if one is used inside a module the result is a reference to a class inside that module. This reference pins the module in memory preventing unloading.

In the sample application, you can see that a Style tag is used in the shell application for loading a CSS file that contains some type selectors and some class selectors.

<mx:Style source="app/MyAppStyles.css"/>

If you add the -keep compiled argument, then you will be able to see the style registration code that is auto-generated by the MXML compiler. Note that the specifics of the auto-generated code can change between releases, but with Flex 3.5, a file called MyApp-generate.as will be produced that has a function called _MyApp_StylesInit(), which performs the actual registration.

You'll also notice that no Style tag is used inside the module. Instead, style selectors that are specific to the module are specified in the module/ModuleStyles.css file and compiled into a SWF that is loaded and unloaded together with the module. More details on this later.

Compile Resource Bundles Into The Shell Application

In a similar manner to styles, when the MXML compiler processes some [ResourceBundle] metadata, some code is auto-generated to register the resources from the specified properties file with the ResourceManager singleton. This code creates a reference to the auto-generated class, so if it takes place inside a module, the module will end up being pinned in memory. Instead, the resources need to be compiled into the shell application or else loaded at runtime using a resource module.

In the sample application, the resources for the module are compiled into the shell application:

<mx:Metadata>
    [ResourceBundle("app")]
    [ResourceBundle("module")]
</mx:Metadata>

If the resource bundles are not large and are known at compile time, and you don't need to switch locale at runtime, then this is the simplest approach. If the resource bundles are large, unknown at compiled time or you do need to switch locale, then using resource modules is a better approach. Refer to the Flex documentation for instructions to compile resource modules. They can be loaded and unloaded via the ResourceManager.

Link Components With Default Styles (or keep all type selectors)

When the MXML compiled encounters a component that has default styling specified in a type-selector of a CSS file inside the Flex SDK or another referenced CSS file, it will auto-generate code to register those styles. If that registration happens inside a module, the module will be pinned in memory in the same way as with a Style tag.

There are two ways to avoid this. The first is to statically link components with default styles into the shell application. Then the default style registration will take place when the application starts up and not when the module is loaded. This can be seen in the sample application:

Button;
RadioButton;
CheckBox;
ComboBox;
TextInput;
DataGrid;
DataGridColumn;
Module;

Alternatively, the -keep-all-type-selectors compiler argument can be used to force the compiler to generate style registration code for all known type selectors, regardless of whether or not the components are linked into the application.

This is one of the areas of improvement in Flex 4. Instead of registering style selectors with the StyleManager singleton, Flex 4 features a revised styles sub-system that registers styles with each module, preventing the issue described above.

Link Classes with RemoteClass Metadata

When [RemoteClass] metadata is encountered, the MXML compiler auto-generates code to register the remote class mapping with Flash Player. Before Flash Player 10.1b3, this resulted in a reference that can prevent module unloading. To avoid this issue with earlier versions of Flash Player, classes with such metadata can be linked into the shell application. Then the remote class registration takes place at application start-up and doesn't pin the module in memory.

The use of HTTPService in the module appears to cause this issue, presumably because it links other classes with [RemoteClass] metadata. For this reason, the HTTPService is statically linked into the shell application too:

HTTPService;

This issue has been resolved in Flash Player 10.1b3. For earlier versions, the above work-around can be used. An alternative approach that also works is to compile the remote classes into a separate module that is loaded at start-up.

Move Focus Out of Modules Before Unloading

The Flex FocusManager holds a reference to the component currently in focus. If that component is inside a module, then that reference will pin the module in memory preventing unloading. When a module is unloaded it is necessary to move the focus onto a component outside of the module. This is the user experience that would generally be desired anyway, so the next appropriate control is in focus for the user to continue their work.

In the sample application, a TextInput has been declared:

<mx:TextInput id="textInput"/>

Just before unloading takes place, the focus is moved to this TextInput:

textInput.setFocus();

There are also cases where Flash Player will hold a reference to the last interactive control, but this will be released as the user proceeds to interact with other parts of the application.

Load Modules Into Child Application Domains

When a module or compiled CSS SWF is loaded, an application domain can be given. This is the application domain into which the class definitions inside the SWFs are loaded and stored. These class definitions can only be unloaded when no references to instances remain and no reference to the application domain itself remains. For this reason, it is important to load modules into a child application domain and not the current application domain if you wish to unload them. The current application domain contains the shell application classes and can never be made eligible for garbage collection.

In the sample application, a child application domain is created, then used for loading both the module and compiled CSS SWFs:

var childDomain:ApplicationDomain = 
    new ApplicationDomain(ApplicationDomain.currentDomain);

ml.applicationDomain = childDomain;
ml.loadModule("module/MyModule.swf");

StyleManager.loadStyleDeclarations(
    "module/MyModuleStyles.swf", 
    true, false, childDomain);

When it comes to unloading, it is important to nullify the reference that the ModuleLoader component holds to the child ApplicationDomain, so it can be garbage collected:

ml.applicationDomain = null;

For more information about application domains and their subtle nuances, refer to the following blog posts:

Use Compiled CSS for Module Styling

When the MXML compiler is used to compile a CSS file into a SWF, the generate code contains both registration and de-registration functions. This means that compiled CSS supports unloading, so it is a good approach for styling modules. Any selectors that are unique to the module can be compiled into a stylesheet SWF that is loaded together with the module, then unloaded again afterwards.

This approach can be seen in the sample application. The module/MyModuleStyles.css is compiled into module/MyModuleStyles.swf and the StyleManager is used to load the SWf into a child application domain and unload it again afterwards:

StyleManager.loadStyleDeclarations(
    "module/MyModuleStyles.swf", 
    true, false, childDomain);
...
StyleManager.unloadStyleDeclarations(
    "module/MyModuleStyles.swf");

Consider Forcing the Garbage Collector

When the unload() method is called to unload a module, there is no guarantee that it will be immediately garbage collected. If there are no remaining references, then it will becomes eligible for garbage collection (GC) and may be removed later, when Flash Player chooses to run the garbage collector. The System.gc() method will force the garbage collector to run in debug versions of Flash Player, but there is no such API for release versions at present.

There is however an unsupported hack known to prompt the garbage collector in most versions of Flash Player. This is documented, along with various caveats, in Grant Skinner's blog post:

By attempting to open 2 local connections then swallowing the resulting error, a full mark-and-sweep garbage collection is instructed:

try
{
    new LocalConnection().connect("anything");
    new LocalConnection().connect("anything");
}
catch (e:*) {} // ignore intentionally

The sample application contains a check-box that allows you to control whether or not this hack is applied after each unload. In my simple tests, the memory footprint remained lower when the hack was applied than without, but I have not conducted exhaustive testing and this technique is unsupported by Adobe. Note however that there is an enhancement request to provide an official API for forcing the garbage collector, which you are free to vote for:

JP Auclair's blog contains a detailed explanation of the Flash Player garbage collection algorithm:

Understanding the Expected Behaviour

When profiling module unloading, it is important to understand the expected behaviour and not misinterpret the memory footprint of the browser process as indicative of a memory leak. When a module is unloaded and the garbage collector has cleaned it up, the memory footprint should not be expected to return all the way to the base-line level. This is because the pages of memory that Flash Player and the browser have allocated from the operating system may not be completely empty. Blocks within these pages may have been allocated to different objects in the application. Only empty pages can be returned to the operating system. So after unloading and garbage collection you should expect some reduction, but when the next module is loaded, the holes in the existing pages will be filled before more pages are grabbed from the operating system. Overall the memory footprint is expected to be lower when applying unloading than without.

Unloading is Not Always Necessary

It should be noted that for some applications unloading is not necessary and a load-once policy may be adequate. Furthermore, some applications may require more elaborate policies, where some modules are unloaded, but the modules most frequently used by users are always kept in memory. Remember that there are many benefits to modularization, not least of which is the efficiency gains for developers working on distinct functional areas that can be developed, tested and profiled in greater isolation.

If an application has a relatively small number of modules and the overall memory footprint when every module has been loaded is acceptable, then perceived performance will be better and programming easier if unloading is not used. Similarly, if a user is only entitled to access a subset of the available modules, it may be acceptable to leave them in memory after loading. It is better to load-once-and-leave rather than unsuccessfully attempting to unload and creating a memory leak.

Light-Touch Optimization

There are many ways to optimize a Flex application to improve perceived performance and reduce resource consumption. However, optimization can often have an impact on code simplicity and clarity, so the benefit needs to outweigh this cost. The best kind of optimization has a light touch on the existing code base. Where manual clean-up code ot hacks are deemed necessary, it is best to try and isolate this code and not to allow it to spread widely through the codebase. For instance, the extensive use of dispose() methods has sometimes been advocated, but this typically complicates a large number of application classes. The example in this blog post gets by without such methods.

Conclusion

Flex provides simple yet powerful features for building modular applications, but some care needs to be taken to ensure that modules unload effectively. This article demonstrated some practices for ensuring that styled and localized Flex 3 modules do unload. These can be used to reduce the overall memory footprint of large, modular applications. The latest betas of Flash Player 10.1 and Flex 4 both introduce further improvements that help to simplify memory management, such as the modular styles sub-system in Flex 4 and the new explicit disposal APIs in Flash Player 10.1.

For more information about memory management in Flex, refer to the comprehensive series of blog posts written by Alex Harui. These are essential reading for enterprise Flex developers:

Posted by tsugden at 9:58 PM | Comments (0)