10 Replies Latest reply on Nov 30, 2009 1:48 PM by Flex harUI

    Profiling the loading and unloading of modules

    kiwiJester

      Modules appear to be the ideal solution for building complex rich internet applications but first hand experience has also shown me that they can leak memory like nothing else. I've read anything and everything I could find about modules and module loading/unloading including of Alex Harui's blog post "What We Know About Unloading Modules" that reveals a number of potential leak causes that should be considered.

       

      I've now created a simple soak test that repeatedly loads and unloads a specified module to help identify memory leaks using the profiler. However, even with the most basic of modules, I find that memory usage will steadily grow. What I'd like to know is what memory stuff is unavoidable flex overhead associated with the loading of modules and what memory stuff am I guilty for, for not cleaning up object references? I'd like to be able to establish some baseline values to which I will be able to compare future modules against.

       

      I've been following the approach suggested in the Adobe Flash Builder 4 Reference page "Identifying problem areas"

       

       

      "One approach to identifying a memory leak is to first find a discrete set of steps that you can do over and over again with your application, where memory usage continues to grow. It is important to do that set of steps at least once in your application before taking the initial memory snapshot so that any cached objects or other instances are included in that snapshot."

       

       

       

      Obviously my set of discrete steps is the loading and unloading of a module. I load and unload the module once before taking a memory snapshot. Then I run my test that loads and unloads the module a large number of times and then take another snapshot.

       

      After running my test on a very basic module for 200 cycles I make the following observations in the profiler:

      Live Objects:

      ClassPackage (Filtered)Cumulative InstancesInstancesCumulative MemoryMemory
      _basicModule_mx_core_FlexModuleFactory201 (1.77%)201 (85.17%)111756 (24.35%)

      111756 (95.35%)

      What ever that _basicModule_mx_core_FlexModuleFactory class is, it's 201 instances end up accounting for over 95% of the memory in "Live Objects".

       

      Loitering Objects:

      ClassPackageInstancesMemory
      Class
      600 (9.08%)2743074 (85.23%)
      _basicModule_mx_core_FlexModuleFactory200 (3.03%)111200 (3.45%)

      However this data suggests that the _basicModule_mx_core_FlexModuleFactory class is the least of my worries, only accounting for 3.45% of the total memory in "Loitering Objects". Compare that to the Class class with it's 600 instances consuming over 85% of the memory. Exploring the Class class deeper appears to show them all to be the [newclass] internal player actions.

       

      Allocation Trace:

      MethodPackage (Filtered)Cumulative InstancesSelf InstancesCumulative MemorySelf Memory
      [newclass]1200 (1.39%)1200 (14.82%)2762274 (13.64%)2762274 (62.76%)

      This appears to confirm the observations from the "Loitering Objects" table, but do I have any influence over the internal player actions?

       

       

      So this brings me back to my original question:

      What memory stuff is unavoidable flex overhead associated with the loading of modules and what memory stuff am I guilty for, for not cleaning up object references? If these are the results for such a basic module, what can I really expect for a much more complex module? How can I make better sense of the profile data?

       

       

      This is my basic module soak tester (sorry about the code dump but there's not that much code really):

      basicModule.mxml

       

      <?xml version="1.0" encoding="utf-8"?>
      <mx:Module xmlns:fx="http://ns.adobe.com/mxml/2009"
                 xmlns:s="library://ns.adobe.com/flex/spark"
                 xmlns:mx="library://ns.adobe.com/flex/halo"
                 layout="absolute" width="400" height="300"
                 backgroundColor="#0096FF" backgroundAlpha="0.2">
      
           <s:Label x="165" y="135" text="basicModule" fontSize="20" fontWeight="bold"/>
      
      </mx:Module>
      

       

      moduleSoakTester.mxml

       

      <?xml version="1.0" encoding="utf-8"?>
      <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
                     xmlns:s="library://ns.adobe.com/flex/spark"
                     xmlns:mx="library://ns.adobe.com/flex/halo"
                     width="400" height="300" backgroundColor="#D4D4D4"
                     initialize="application_initializeHandler(event)">
           
           <fx:Script>
                <![CDATA[
                     import mx.events.FlexEvent;
                     import mx.events.ModuleEvent;
                     [Bindable]
                     public var loadCount:int = -1;
                     [Bindable]
                     public var unloadCount:int = -1;
                     public var maxCycles:int = 200;
                     public var loadTimer:Timer = new Timer(500, 1);
                     public var unloadTimer:Timer = new Timer(500, 1);
      
                     protected function application_initializeHandler(event:FlexEvent):void
                     {
                          loadTimer.addEventListener(TimerEvent.TIMER_COMPLETE, loadTimer_timerCompleteHandler);
                          unloadTimer.addEventListener(TimerEvent.TIMER_COMPLETE, unloadTimer_timerCompleteHandler);
                     }
      
                     protected function loadModule():void
                     {
                          if(loadCount < maxCycles)
                          {
                               moduleLoader.url = [correctPath] + "/basicModule.swf";
                               moduleLoader.loadModule();
                               loadCount++;
                          }
                     }
      
                     protected function unloadModule():void
                     {
                          moduleLoader.unloadModule();
                          unloadCount++;
                     }
      
                     protected function load_clickHandler(event:MouseEvent):void
                     {
                          load.enabled = false;
                          loadModule();
                          unload.enabled = true;
                     }
      
                     protected function unload_clickHandler(event:MouseEvent):void
                     {
                          unload.enabled = false;
                          unloadModule();
                          run.enabled = true;
                     }
      
                     protected function run_clickHandler(event:MouseEvent):void
                     {
                          run.enabled = false;
                          moduleLoader.addEventListener(ModuleEvent.READY, moduleLoader_readyHandler);
                          moduleLoader.addEventListener(ModuleEvent.UNLOAD, moduleLoader_unloadHandler);
                          loadTimer.start();
                     }
      
                     protected function moduleLoader_readyHandler(event:ModuleEvent):void
                     {
                          unloadTimer.start();
                     }
      
                     protected function moduleLoader_unloadHandler(event:ModuleEvent):void
                     {
                          loadTimer.start();
                     }
      
                     protected function loadTimer_timerCompleteHandler(event:TimerEvent):void
                     {
                          loadModule();
                     }
      
                     protected function unloadTimer_timerCompleteHandler(event:TimerEvent):void
                     {
                          unloadModule();
                     }
                ]]>
           </fx:Script>
      
           <mx:ModuleLoader id="moduleLoader"/>
      
           <s:VGroup x="20" y="20">
                <s:HGroup>
                     <s:Button id="load" label="Load" click="load_clickHandler(event)" enabled="true"/>
                     <s:Button id="unload" label="Unload" click="unload_clickHandler(event)" enabled="false"/>
                     <s:Button id="run" label="Run" click="run_clickHandler(event)" enabled="false"/>
                </s:HGroup>
                <s:Label text="loaded: {loadCount.toString()}" fontSize="15"/>
                <s:Label text="unloaded: {unloadCount.toString()}" fontSize="15" x="484" y="472"/>
           </s:VGroup>
      
      </s:Application>
      

       

       

      Cheers,

      -Damon