1 Reply Latest reply on Mar 2, 2010 9:24 AM by Jaylach

    Memory Leak in custom Flex 4 itemRenderer

    Jaylach

      Hi,

       

      I am running into a horrible memory leak when using a custom itemRenderer created in Flex 4. I've created a custom itemRenderer that's used to display a thumbnail of a PowerPoint slide which gets exported on the server by ASP.NET. We are displaying these thumbnails in a custom Flex 4 list using the TileLayout layout.

       

      My current test is with loading 44 of these into the list. At the time the data is loaded into the list I see the memory usage in IE jump to about 200k+. When I profile the application I see the same huge jump. It's starts to get more interesting when I attempt to drag these images around the list (I have dragMove enabled). When I start dragging I see the count of the itemRenderer instances (in the profile) increment by one. When I drop the image I again see the count of the instances increment by 1.

       

      If I take a memory snap shot of when the images are first loaded, and then one again after I've moved a few images around, I see anywhere between 20 and 80 BitmapData objects in the "Loitering Objects" view. The memory column in the Loitering Objects view for these items reads over 800,000. I cannot, for the life of me, figure out why these BitmapData objects are continuing to lay around and why they are using so much memory.

       

      I've copied the ItemRenderer code into this post, maybe someone will be able to figure out what I'm doing wrong.

       

      Many thanks in advance!!!

       

       

      <?xml version="1.0" encoding="utf-8"?>

      <s:ItemRenderer

       

      xmlns:fx="http://ns.adobe.com/mxml/2009"

      xmlns:s="

      library://ns.adobe.com/flex/spark"

      xmlns:mx="

      library://ns.adobe.com/flex/halo"

      xmlns:ns="

      library://ns.adobe.com/flex/mx"

      click="clickHandler();"

      mouseMove="mouseMoveHandler(event);"

      >

       

      <!-- Scripts -->

       

      <fx:Script>

      <![CDATA[

       

      import modules.presentations.Resources;

       

      import modules.presentations.components.ExSlideSorter;

       

      import modules.presentations.dataObjects.Bundle;

       

      import modules.presentations.dataObjects.Slide;

       

      import modules.presentations.events.ExSlideSorterEvent;

       

       

      import mx.collections.ArrayCollection;

       

      import mx.controls.Image;

       

      import mx.graphics.SolidColor;

       

       

      /* -------------------------

      * Fields

      * ------------------------- */

       

      private var _slide:Slide = null;

       

      /* -------------------------

      * Event Handlers

      * ------------------------- */

       

      /** @private **/

       

      private function mouseMoveHandler(e:MouseEvent):void {

       

      // Create our event data

       

      var eventData:ArrayCollection = new ArrayCollection();

      eventData.addItem(e);

      eventData.addItem(

      this);

       

      // Dispatch our event

      owner.dispatchEvent(

      new ExSlideSorterEvent(ExSlideSorterEvent.ITEM_MOUSE_MOVE, false, false, eventData));

      }

       

      /** @private **/

       

      private function clickHandler():void {

      owner.dispatchEvent(

      new ExSlideSorterEvent(ExSlideSorterEvent.ITEM_CLICK, false, false, this));

      }

       

      /** @private **/

       

      private function bundleGroupClickHandler(e:Event):void {

       

      if ( _slide.isPartOfBundleType(Bundle.BUNDLE_TYPE_UNORDERED) ) {

       

      var bundleIndex:Number = _slide.getBundleIndexByType(Bundle.BUNDLE_TYPE_UNORDERED);

      ExSlideSorter(owner).selectBundle(_slide.getBundleByIndex(bundleIndex));

      }

      }

       

      /** @private **/

       

      private function bundleSlidesBetweenSelected(e:ExSlideSorterEvent):void {

       

      if ( _slide.isPartOfBundleType(Bundle.BUNDLE_TYPE_SLIDES_BETWEEN) ) {

       

      // Determine if we're part of any of the bundles selected

       

      var partOfBundle:Boolean = false;

       

      var arrayBundles:ArrayCollection = ArrayCollection(e.data);

       

      for ( var a:Number = 0; a <= arrayBundles.length - 1; a++ ) {

       

      if ( _slide.isPartOfBundle(arrayBundles[a]) ) {

      partOfBundle =

      true;

       

      break;

      }

      }

       

      // Show our border if we're part of the bundle

       

      if ( partOfBundle && currentState != "hovered" && currentState != "selected" ) {

      currentState =

      "hovered";

      }

      }

      }

       

      /** @private **/

       

      private function bundleSlidesBetweenUnselected(e:ExSlideSorterEvent):void {

       

      if ( _slide.isPartOfBundleType(Bundle.BUNDLE_TYPE_SLIDES_BETWEEN) ) {

       

      // Determine if we're part of any of the bundles selected

       

      var partOfBundle:Boolean = false;

       

      var arrayBundles:ArrayCollection = ArrayCollection(e.data);

       

      for ( var a:Number = 0; a <= arrayBundles.length - 1; a++ ) {

       

      if ( _slide.isPartOfBundle(arrayBundles[a]) ) {

      partOfBundle =

      true;

       

      break;

      }

      }

       

      // Show our border if we're part of the bundle

       

      if ( partOfBundle && currentState == "hovered" ) {

      currentState =

      "normal";

      }

      }

      }

       

      private function slideBundleUpdatedHandler(e:ExSlideSorterEvent):void {

       

      // Get our slide and determine bundles

       

      if ( _slide.isPartOfBundle() ) {

      determineSlideBundles();

      determineSlideBundleRules();

      }

      else {

       

      // Hide our bundle bars

      showBundleBar(

      "bundleLeft", false);

      showBundleBar(

      "bundleRight", false);

      }

      }

       

      /* -------------------------

      * Private Functions

      * ------------------------- */

       

      /** @private **/

       

      private function determineSlideBundles():void {

       

      var fillColor:SolidColor = new SolidColor(0, 0.0);

       

      // Determine the border and fill colors based on the type of bundle the LAST bundle is

       

      var bundle:Bundle = Bundle(_slide.Bundles[_slide.Bundles.length - 1]);

       

      if ( bundle.BundleType == Bundle.BUNDLE_TYPE_ORDERED ) {

      fillColor =

      new SolidColor(0xE10404, 1.0);

      }

      else if ( bundle.BundleType == Bundle.BUNDLE_TYPE_UNORDERED ) {

      fillColor =

      new SolidColor(0x05e010, 1.0);

      }

       

      // Determine which bars to show

       

      if ( bundle.Slides.indexOf(_slide) == 0 ) {

       

      // First slide, show the Left Cap and the Right Bar

      showBundleBar(

      "bundleLeft", true, fillColor);

      showBundleBar(

      "bundleRight", true, fillColor);

       

      // Round out our Left Bar

      bundleLeftBar.left = 2;

      bundleLeftBar.radiusX = 7;

      }

      else if ( bundle.Slides.indexOf(_slide) == bundle.Slides.length - 1 ) {

       

      // Last slide, show the Right Cap and the Left Bar

      showBundleBar(

      "bundleLeft", true, fillColor);

      showBundleBar(

      "bundleRight", true, fillColor);

       

      // Round out our Right Bar

      bundleRightBar.right = 2;

      bundleRightBar.radiusX = 7;

      }

      else {

       

      // Middle slide, show the Right and Left bars

      showBundleBar(

      "bundleLeft", true, fillColor);

      showBundleBar(

      "bundleRight", true, fillColor);

      }

       

      // Determine if we're part of a Slides Between bundle

       

      if ( _slide.isPartOfBundleType(Bundle.BUNDLE_TYPE_SLIDES_BETWEEN) ) {

      slidesBetweenRect.visible =

      true;

      }

      }

       

      /** @private **/

       

      private function determineSlideBundleRules():void {

       

      if ( _slide.isPartOfLockedBundle() && (_slide.getBundlePosition(_slide.getLockedBundleIndex()) == 0) ) {

       

      var ruleTip:String = "This bundle is LOCKED in it's current position";

       

      // Show our Icon

      bundleLockedIconHolder.height = 20;

      bundleLockedIconHolder.visible =

      true;

      bundleLockedIconHolder.toolTip = ruleTip;

       

      // Show the rule bar

      bundleRuleBar.visible =

      true;

      bundleRuleBar.verticalCenter = 0

      }

      else {

      bundleLockedIconHolder.visible =

      false;

      bundleRuleBar.visible =

      false;

      }

      }

       

      /** @private **/

       

      private function determineSlideRules():void {

       

      var ruleCount:Number = 0;

       

      var ruleTip:String = "";

       

      // Should we show our "Editable Slide" icon?

       

      if ( _slide.Rules.IsEditable ) {

      ruleTip =

      "This slide is EDITABLE by the user";

       

      // Show our Icon

      slideEditableIconHolder.width = 20;

      slideEditableIconHolder.visible =

      true;

      slideEditableIcon.toolTip = ruleTip;

      ruleCount++;

      }

      else {

      slideEditableIconHolder.width = 0;

      slideEditableIconHolder.visible =

      false;

      }

       

      // Should we show our "Locked Slide" icon?

       

      if ( _slide.Rules.IsLocked ) {

       

      // Only set our toolTip if it has not yet been set

       

      if ( (slideLockedIcon.toolTip == null) || (slideLockedIcon.toolTip.length == 0) ) {

      ruleTip =

      "This slide is LOCKED in it's current position";

      slideLockedIcon.toolTip = ruleTip;

      }

       

      // Show our Icon

      slideLockedIconHolder.width = 20;

      slideLockedIconHolder.visible =

      true;

      ruleCount++;

      }

      else {

      slideLockedIconHolder.width = 0;

      slideLockedIconHolder.visible =

      false;

      }

       

      // Should we show our "Mandatory Slide" icon?

       

      if ( _slide.Rules.IsMandatory ) {

      ruleTip =

      "This slide is MANDATORY";

       

      // Show our Icon

      slideMandatoryIconHolder.width = 20;

      slideMandatoryIconHolder.visible =

      true;

      slideMandatoryIcon.toolTip = ruleTip;

      ruleCount++;

      }

      else {

      slideMandatoryIconHolder.width = 0;

      slideMandatoryIconHolder.visible =

      false;

      }

       

      // Should we show our Rule bar?

       

      if ( ruleCount > 0 ) {

      slideRuleBar.visible =

      true;

      }

      }

       

      /**

      * The showBundleBar function will show or hide the bundle bar at the specified position.

      * The position can either be "bundleLeft" or "bundleRight".

      *

      * @private

      * **/

       

      private function showBundleBar(barPosition:String, visible:Boolean, fill:SolidColor=null):void {

       

      var barName = barPosition + "Bar";

       

      var barRect:Rect = Rect(this[barName]);

       

      //var barGroup:Group = Group(this[barPosition + "Group"]);

       

      if ( visible ) {

       

      //barGroup.visible = true;

      barRect.visible =

      true;

      barRect.fill = fill;

      barRect.radiusX = 0;

       

      if ( barPosition.indexOf("Left") >= 0 ) {

      barRect.left = 0;

      }

      else if ( barPosition.indexOf("Right") >= 0 ) {

      barRect.right = 0;

      }

      }

      else {

       

      //barGroup.visible = false;

      barRect.visible =

      false;

      }

      }

       

      /* -------------------------

      * Override Functions

      * ------------------------- */

       

      /** @private **/

       

      protected override function stateChanged(oldState:String, newState:String, recursive:Boolean):void {

       

      super.stateChanged(oldState, newState, recursive);

       

      if ( newState == "hovered" || newState == "selected" ) {

       

      if ( _slide.isPartOfBundleType(Bundle.BUNDLE_TYPE_SLIDES_BETWEEN) ) {

       

      // Get our list of Slides Between bundles

       

      var arrayBundles:ArrayCollection = new ArrayCollection();

       

      for ( var a:Number = 0; a <= _slide.Bundles.length - 1; a++ ) {

       

      var bundle:Bundle = _slide.getBundleByIndex(a);

       

      if ( bundle.BundleType == Bundle.BUNDLE_TYPE_SLIDES_BETWEEN ) {

      arrayBundles.addItem(bundle.BundleID);

      }

      }

       

      // Dispatch our event

      ExSlideSorter(owner).dispatchEvent(

      new ExSlideSorterEvent(ExSlideSorterEvent.ITEM_SBTW_BUNDLE_SELECT, false, false, arrayBundles));

      }

      }

      else {

       

      if ( _slide.isPartOfBundleType(Bundle.BUNDLE_TYPE_SLIDES_BETWEEN) ) {

       

      // Get our list of Slides Between bundles

       

      var arrayBundles:ArrayCollection = new ArrayCollection();

       

      for ( var a:Number = 0; a <= _slide.Bundles.length - 1; a++ ) {

       

      var bundle:Bundle = _slide.getBundleByIndex(a);

       

      if ( bundle.BundleType == Bundle.BUNDLE_TYPE_SLIDES_BETWEEN ) {

      arrayBundles.addItem(bundle.BundleID);

      }

      }

       

      // Dispatch our event

      ExSlideSorter(owner).dispatchEvent(

      new ExSlideSorterEvent(ExSlideSorterEvent.ITEM_SBTW_BUNDLE_UNSELECT, false, false, arrayBundles));

      }

      }

      }

       

      /** @private **/

       

      public override function set data(value:Object):void {

       

      super.data = value;

       

      if ( value != null ) {

      _slide = Slide(data);

      thumbnail.source = _slide.ThumbnailURL;

       

      // Show what we should show

       

      if ( _slide != null ) {

       

      // Get our slide and determine bundles

       

      if ( _slide.isPartOfBundle() ) {

      determineSlideBundles();

      determineSlideBundleRules();

      }

      else {

       

      // Hide our bundle bars

      showBundleBar(

      "bundleLeft", false);

      showBundleBar(

      "bundleRight", false);

      }

       

      // Determine our rules

       

      if ( _slide.Rules != null ) {

      determineSlideRules();

      }

      }

       

      // Listen for our Events

      owner.addEventListener(ExSlideSorterEvent.ITEM_BUNDLE_UPDATED, slideBundleUpdatedHandler,

      false, 0, true);

      owner.addEventListener(ExSlideSorterEvent.ITEM_SBTW_BUNDLE_SELECT, bundleSlidesBetweenSelected,

      false, 0, true);

      owner.addEventListener(ExSlideSorterEvent.ITEM_SBTW_BUNDLE_UNSELECT, bundleSlidesBetweenUnselected,

      false, 0, true);

      }

      }

      ]]>

       

      </fx:Script>

       

      <!-- States -->

       

      <s:states>

       

      <s:State name="normal"/>

       

      <s:State name="hovered"/>

       

      <s:State name="selected"/>

       

      </s:states>

       

      <!-- The main container group -->

       

      <s:Group id="mainGroup" left="0" right="0" top="0" bottom="0">

       

      <!--

      Layer 0: Bundle Bars

      -->

       

      <s:Group id="bundleLeftGroup" height="16" width="16" verticalCenter="0" left="0" click="bundleGroupClickHandler(event);">

       

      <s:Rect id="bundleLeftBar" left="0" verticalCenter="0" width="16" height="16" />

       

      </s:Group>

       

      <!-- Right Bundle -->

       

      <s:Group id="bundleRightGroup" height="16" width="16" verticalCenter="0" right="0" click="bundleGroupClickHandler(event);">

       

      <s:Rect id="bundleRightBar" right="0" verticalCenter="0" width="16" height="16" />

       

      </s:Group>

       

      <!--

      Layer 1: RollOver / Selection Group

      -->

       

      <s:Rect id="selector" top="5" bottom="5" left="5" right="5"

      visible="

      false" visible.hovered="true" visible.selected="true">

       

      <!-- Fill -->

       

      <s:fill>

       

      <s:SolidColor id="selectorFill"

      color.hovered="

      0xDBD8DE"

      color.selected="

      0xA59AAB" />

       

      </s:fill>

       

      </s:Rect>

       

      <!--

      Layer 2: Slides Between Border

      -->

       

      <s:Rect id="slidesBetweenRect" verticalCenter="0" horizontalCenter="0"

      left="

      7" right="7" top="7" bottom="7" visible="false"

      left.hovered="

      4" right.hovered="4" top.hovered="4" bottom.hovered="4">

       

      <!-- Fill -->

       

      <s:fill>

       

      <s:SolidColor color="0x058BE0" alpha="0.50" alpha.hovered="1.0" alpha.selected="0" />

       

      </s:fill>

       

      </s:Rect>

       

      <!--

      Layer 2: Image

      -->

       

      <ns:Image id="thumbnail" verticalCenter="0" horizontalCenter="0"

      scaleContent="

      true" maintainAspectRatio="false"

      left="

      9" right="9" top="9" bottom="9">

       

      </ns:Image>

       

      <!-- Layer 3: Slide Rule Bar -->

       

      <s:HGroup id="slideRuleBar" height="24" gap="0" top="-4" left="0"

      visible="

      false" alpha="0.75" alpha.hovered="1.0" alpha.selected="1.0">

       

      <!-- Editable Icon -->

       

      <s:Group id="slideEditableIconHolder" height="24" width="20" visible="false">

       

      <ns:Image id="slideEditableIcon"

      width="

      16"

      height="

      16"

      verticalCenter="

      0"

      horizontalCenter="

      0"

      source="

      {Resources.ICON_SLIDE_EDITABLE}" />

       

      </s:Group>

       

      <!-- Locked Icon -->

       

      <s:Group id="slideLockedIconHolder" height="24" width="20" visible="false">

       

      <ns:Image id="slideLockedIcon"

      width="

      16"

      height="

      16"

      verticalCenter="

      0"

      horizontalCenter="

      0"

      source="

      {Resources.ICON_SLIDE_LOCKED}" />

       

      </s:Group>

       

      <!-- Mandatory Icon -->

       

      <s:Group id="slideMandatoryIconHolder" height="24" width="20" visible="false">

       

      <ns:Image id="slideMandatoryIcon"

      width="

      16"

      height="

      16"

      verticalCenter="

      0"

      horizontalCenter="

      0"

      source="

      {Resources.ICON_SLIDE_MANDATORY}" />

       

      </s:Group>

       

      </s:HGroup>

       

      <!-- Layer 4: Bundle Rule Bar -->

       

      <s:HGroup id="bundleRuleBar" width="24" gap="0" left="1" verticalCenter="0"

      visible="

      false" alpha="0.75" alpha.hovered="1.0" alpha.selected="1.0">

       

      <!-- Locked Icon -->

       

      <s:Group id="bundleLockedIconHolder" height="24" width="20" visible="false">

       

      <ns:Image id="bundleLockedIcon"

      width="

      16"

      height="

      16"

      verticalCenter="

      0"

      horizontalCenter="

      0"

      source="

      {Resources.ICON_SLIDE_LOCKED}" />

       

      </s:Group>

       

      </s:HGroup>

       

      <!-- Layer 5: Slide Number -->

       

      <s:Group id="slideNumberGroup" width="24" height="24"

      right="

      0" top="-4"

      visible="

      false" visible.hovered="true">

       

      <!-- Background / Border -->

       

      <s:Rect left="0" right="0" top="2" bottom="2">

       

      <s:fill>

       

      <s:SolidColor color="0x5D5B5E" alpha="0.80" />

       

      </s:fill>

       

      </s:Rect>

       

      <!-- Label -->

       

      <s:Label id="slideNumberLabel" color="0xFFFFFF"

      fontSize="

      12" fontWeight="bold"

      verticalCenter="

      1" horizontalCenter="1"

      text="

      {Slide(data).CurrentPosition}" />

       

      </s:Group>

       

      </s:Group>

      </s:ItemRenderer>