Why? Because you want to incrementally deliver content
A typical model for building a Flex application is to just use one SWF file. That is because by default when you create a Flex project in Flex Builder your main application MXML and all of the classes that it references are compiled into a single SWF file.
Flex Builder then embeds that SWF file in an HTML page where you can then upload it and the Flex content somewhere, and when people visit that page it and the SWF file are downloaded by their browser and Flex application is executed.
It helps to conceptualize as an application in terms of its views or screens. For example an application may consist of a screen to login, a screen to create an account, and so on. In the single SWF model that single file contains all of these different views. You begin to get into trouble when you have a lot of different views, which causes the single SWF file to become large.
For example you could have an application that has 10 different views where each view adds about 200 KB to the application size, the result is that you end up with a 1.95 MB file that the user has to download at one time to run it. This of course would be come a problem when you have a lot more views, views that are larger, or a combination of both.
Flex has the ability to use Modules, which allows you to break an application up into parts. Typically one would break an application up into its views (or screens). The result is an application that the end user downloads piece by piece and as needed. For large applications this is a requirement, since users don’t want to have to wait for several minutes of downloading every time they need to use your application.
I really haven’t been ever able to find a definitive source that explains all of the different ways to use modules, which is why I am writing this. When it comes to modules there are two general approaches:
- The text-book approach – This is the way modules are described in documentation and other Adobe writings, which does not take into account real world functional uses problems.
- The pragmatic approach – This is the way they are actually used in functioning applications, taking into account the plethora of bugs.
I will attempt to explain both approaches, with more emphasis on the pragmatic since a working application accompanies this writing.
Modules and Application Domains
Application Domains in the case of modules are used to determine what parts of a module are actually unloaded when they are unloaded. The following are the two methods in which this functionality is controlled:
Current Domain Method – This makes it do modules never really unload, and that their definitions are kept around for the lifetime of the application. The result is modules load faster the second time they are loaded because they never really unloaded in the first place.
Copied Domain Method – This is the default behavior and is supposed to allow modules to truly unload. The result is that modules always take the same amount of time to after unload since they have to be completely reloaded.
For more information see the following: http://jvalentino.blogspot.com/2009/06/flex-memory-issue-4-modules-and.html
The Realities of Modules
Basically looking at a module wrong will cause it not to unload when using the Copied Domain (Default) Method. The following are culprits for the modules persisting, each of which I have implemented resolutions:
- State and AddChild – This can cause a leak, sort of, see http://www.nbilyk.com/flex-states-memory-leak
- Bindings – Bindings can sometime leak, see http://bugs.adobe.com/jira/browse/SDK-14875
- Forcing Garbage Collection – You have to force GC most of the time to get modules to move along, see http://jvalentino.blogspot.com/2009/05/flex-memory-issue-3-garbage-collection.html
- Listeners – You have to remove listeners when you are done with them.
- Using the Current Domain Method – prevents modules from truly unloading, see http://jvalentino.blogspot.com/2009/06/flex-memory-issue-4-modules-and.html
- Effects – Effects in a module will cause it to leak, see http://jvalentino.blogspot.com/2009/05/flex-memory-issue-2-component-effects.html
Even with these resolutions there is ultimately a problem that is an article unto itself regarding how the memory required to render components becomes fragmented over time, eventually preventing objects that should be eligible for collection from by collected. I will get around writing about it and the proof of its existence later, but for right now just be aware of its effects. Its effects are most noticeable in Flex applications that run at high resolutions, because the larger in size the component the more memory that is required for rendering it.
This basically means that in larger and/or graphically rich applications if you have to render a lot of content over and over you will run out of memory and there is nothing you can do about it. Using the current domain method will mitigate this problem, but then you have to be sure that your application’s definitions can fit within whatever the requirement is for client memory. Even then you will still encounter the problem within rendering memory fragmentation.
What can be done?
Pool your modules.
Once a module has been created store its instance and then retrieve it the next time it is needed. This however requires enough memory available to be able to go to every module in the application, or however modules it is possible to go to.
The result is a memory profile that looks like the following with modules in which the instance never go away:
Using this method you can load modules over and over again without having to worry about the issue with rendering memory fragmentation (unless there is a substantial amount of dynamic manually drawn content).
The resulting secondary load times are also much faster than when using the Current Domain Method:
What are my options?
- Pool your modules or don’t – If you pool your modules then you need to have enough memory available for the possibility that the user loads a lot of modules. Otherwise you are going to hit the problem with rendering memory fragmentation and run out of memory.
- Use the Current Application Domain or the Copied Application Domain Method – If you use the copied domain method it will be extremely difficult to get modules to actually unload, and you will eventually run out of memory in most applications due to the problem with rendering memory fragmentation. If you use the current domain method you will mitigate the the rendering memory fragmentation problem, but you will need to be able to have enough space in memory to deal with the possibility of having to store the definitions for a large number of modules.
- Force Garbage Collection or don’t – If you don’t force garbage collection and you are not pooling modules, then modules sometimes won’t unload. When pooling it will help memory along, but in both cases be aware then forcing garbage collection can cause problems with remoting.
- Cleanup on module unload or don’t – If you don’t “cleanup” when not using pooling then various instances within modules are likely to leak.
How are all these options implemented?
The following is a test application featuring a ModuleManagerUtil class and a MemoryManager class.
Please be aware that the debug version of the Flash Player always leaks modules, so if you are using the debug player just about everything is going to leak. See the following for more information: http://jvalentino.blogspot.com/2009/05/flex-memory-tip-1-debug-player-always.html
About the Sample Application
The ModuleManagerUtil class is used to handle loading and pooling modules.
The MemoryManager class is used to get the current amount of memory in use by the Flash Player and force garbage collection.
The buttons on the left are used to load different modules. The simple modules just contain panels, the complex modules contain DataGrids, and the popup module will load a module as a popup.