23 Replies Latest reply on Feb 26, 2013 9:17 AM by Flex harUI

    Looking for memory leaks

    Zolotoj Level 3

      If I want to profile an app for memory leaks what is the best approach?

       

      Thanks

        • 1. Re: Looking for memory leaks
          Flex harUI Adobe Employee

          See my blog post.  The UI is a bit different in FB 4, but the principles still apply.

          • 2. Re: Looking for memory leaks
            Zolotoj Level 3

            I am following your presentation and want to show two images:

            So, let's say I have the following entry in Loitering Objects:

            Capture.PNG

            Double clicking on it will show this:

            Capture1.PNG

            Where do I go from here?

             

            Also I took this snapshot without touching the app. and compared it to second snapshot:

            Capture2.PNG

            still it shows some objects. Why would that happen at all?

             

            I am adding more things into this thread as I am getitng deeper into memory profiling.

            What would you say if you see something like this:

            Capture3.PNG

            Clicking on it will show the following in the Allocation Trace:

            Capture4.PNG

            Dblclicking on ItemRenderer (line 102) will take to this code:

             

            public function ItemRenderer()

                {

                    super();

                   

                    // Initially state is dirty

                    rendererStateIsDirty = true;

                   

                    interactionStateDetector = new InteractionStateDetector(this);

                   interactionStateDetector.addEventListener(Event.CHANGE, interactionStateDetector_changeHandler);

                }

             

            What exactly does it mean? A leak in SDK? Is a fix possible, in case it is a leak?

             

            Sorry for throwing many things together at once. Dont have much time  ....

             

             

            Thanks

            • 3. Re: Looking for memory leaks
              Flex harUI Adobe Employee

              Looks like event listeners are attached to that object.  Clicking on each function should show the call stack at the time of allocation which should help you find who is attaching listeners.

              • 4. Re: Looking for memory leaks
                Zolotoj Level 3

                Can you please explain more how a process of finding memory leaks show be going? I am getting a list of loitering objects. Then I go into Object references on a particular class from that list. Then I see a bunch of functions after expanding it. Do I have to fix all these functions? Any tips of locating offensive code? Why I am asking it because so may times a line number from Allocation Trace does not really points to anything that makes sense in terms of a memory leak. And speaking about Allocation Trace which line should start from? Very last one of my code? Or last one? Usually these lines are SDK's.

                 

                Thanks

                • 5. Re: Looking for memory leaks
                  Flex harUI Adobe Employee

                  The first and very important point is to understand if your scenario allows the use of Loitering Objects view.  The profiler was designed for “create-and-destroy” scenarios, but some scenarios are cyclical and Loitering Objects will return false positives.

                   

                  The “art” of memory leak analysis is in the determining of which objects to worry about.  It might be ok for ReviewMessages to have 8 listeners if all of the objects involved are going to get tossed along with ReviewMessage together later.

                   

                  But if you are sure that ReviewMessages is not supposed to be around any more but the other objects should, the allocation trace in this case should show you what code attached the listener, and then you have to decide if there is some other API call you were supposed to make to get that code to remove the listener,  or maybe that code needs to use a weak reference listener, or maybe some API for something further down in the allocation trace is what you need to call to get that listener removed.

                  • 6. Re: Looking for memory leaks
                    Zolotoj Level 3

                    Not sure if I can decide which scenario is to go by but I am using the following to see what is causing a memory leak. If I run the same screen two times and I see a loitering object with cumulative number going up I am  assuning this object is memory leak. Am I correct?

                    • 7. Re: Looking for memory leaks
                      Flex harUI Adobe Employee

                      LoiteringObjects does not work for scenarios like this:

                       

                      You have one instance of a screen but every time you switch to it, you replace old instances of child objects with new objects.  LoiteringObjects only shows you what instances are in the second snapshot that weren’t in the first.  Because you created replacement objects, it will show that those are leaking but they aren’t since every time you switch you replace the old with the new.  In such a scenario, I do not use LoiteringObjects and just compare the instance counts between the two snapshots manually.

                      • 8. Re: Looking for memory leaks
                        Zolotoj Level 3

                        I am confused as hell.

                        If I take a memory snapshot before I run a screen and take another one after I close it and check for objects that have cumulitive number of instances encreased is it right to assume that such an object is a cause of a memory leak?

                        • 9. Re: Looking for memory leaks
                          Zolotoj Level 3

                          Can you please take a look at this screen and tell where I suppose to go? The class itself or all these functions one by one?

                          Capture5.PNG

                           

                          Also it shows that all these functions are GCed. How can the class is not?

                          • 10. Re: Looking for memory leaks
                            Flex harUI Adobe Employee

                            Yes, unless your app is collecting records of things on purpose.

                            • 11. Re: Looking for memory leaks
                              Flex harUI Adobe Employee

                              The functions are holding the class in memory.  The allocations should give you a clue.  Feel free to post images of the allocation trace if it is in SDK code.

                              • 12. Re: Looking for memory leaks
                                Zolotoj Level 3

                                Here is one:

                                Capture6.PNG

                                Where exactly in the Allocation Trace would I look?

                                 

                                I am using 4.5.1

                                 

                                Thank you.

                                • 13. Re: Looking for memory leaks
                                  Flex harUI Adobe Employee

                                  Is there an addEventListener call in StatusCountRenderer on line 10?

                                  • 14. Re: Looking for memory leaks
                                    Zolotoj Level 3

                                    StatusCountRenderer

                                     

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

                                    <renderer:StatusCountRendererBase

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

                                                                            xmlns:s="library://ns.adobe.com/flex/spark"

                                                                            xmlns:renderer="renderer.*"

                                                                            xmlns:commons="library://commons.stoneriver.com"

                                                                            autoDrawBackground="true"

                                                                            dataChange="onDataChange(event)"

                                                                            name="CounterRenderer"

                                                                            >

                                     

                                     

                                     

                                              <s:VGroup id="holder" left="3" right="3" top="3" bottom="3" horizontalAlign="center">

                                                        <commons:DropDownListBorderless id="btn"

                                                                                                                                         height="100%" labelPos="center"

                                                                                                                                         creationComplete="createButtonMenu()"

                                                                                                                                         click="btn_mouseDownHandler(event)"

                                                                                                                                            styleName="PolicyStatusCount"

                                                                                                                                         keepPrompt="true" />

                                                        <s:Label id="lbl"

                                                                             height="100%"

                                                                             verticalAlign="middle"

                                                                             styleName="PolicyStatusPopup" />

                                              </s:VGroup>

                                     

                                    </renderer:StatusCountRendererBase>

                                     

                                    and StatusCountRendererBase

                                     

                                    public class StatusCountRendererBase extends ItemRenderer{

                                                        [Bindable] public var btn:CustomDropDownBorderless;

                                                        [Bindable] public var lbl:Label;

                                     

                                                        private var _myIndex:int;

                                     

                                                        public function StatusCountRendererBase(){

                                                                  super();

                                                        }

                                     

                                     

                                                        protected function onDataChange(event:FlexEvent):void {

                                                                  if (data){

                                                                            ResourceManager.getInstance().bindValue(btn, 'prompt', data['statusCount'].nameKey)

                                                                            lbl.text = AgingRangeUtil.getAgingRangeAndCountLabel(

                                                                                      AgingRangeUtil.getAgingRangeByType(data['statusCount'].agingRanges, data['statusCount'].selectedRange));

                                                                  }

                                                        }

                                     

                                     

                                                        protected function createButtonMenu():void{

                                                                  btn.dataProvider = data['agingRanges'];

                                                                  btn.addEventListener(IndexChangeEvent.CHANGE, onMenuChange);

                                                        }

                                     

                                                        protected function btn_mouseDownHandler(event:MouseEvent):void{

                                                                  if(event.target as CustomDropDownButton){

                                                                            event.stopImmediatePropagation();

                                                                  }

                                                        }

                                     

                                                        private function onMenuChange(event:IndexChangeEvent):void {

                                                                  //trace("fix menu change event");

                                                                  var ni:int = event.newIndex;

                                                                  if(ni != -1){

                                                                            var ardto:* = data['agingRanges'][ni];

                                                                            lbl.text = ardto.label;

                                                                            var statCntType:int = data['statusCount'].kind;

                                                                            var rangeType:int = ardto.data.rangeType;

                                                                            this.owner.dispatchEvent(StatusCountControlEvent.newStatusCountRangeChangedEvent(statCntT ype,rangeType));

                                                                  }

                                     

                                                        }

                                              }

                                    • 15. Re: Looking for memory leaks
                                      Flex harUI Adobe Employee

                                      Hmm, didn’t see a match.  Try using –keep-generated-actionscript and see what it thinks is on line 10.

                                      • 16. Re: Looking for memory leaks
                                        Zolotoj Level 3

                                        Where is this " using –keep-generated-actionscript" option?

                                        • 17. Re: Looking for memory leaks
                                          Flex harUI Adobe Employee

                                          It is a compiler option.

                                          • 18. Re: Looking for memory leaks
                                            Zolotoj Level 3

                                            Yes, I recompiled all my projects with this option.

                                            Here are some snapshots from Profiler running after it.

                                            I am showing a different class because this one has much worse memory leak.

                                            Capture7.PNG

                                            After clicking on Instance:

                                            Capture8.PNG

                                            Function 1

                                            Capture8.PNG

                                            Function 2

                                            Capture9.PNG

                                            Function 3

                                            Capture10.PNG

                                            Function 4

                                            Capture11.PNG

                                            Function 5

                                            Capture12.PNG

                                            • 19. Re: Looking for memory leaks
                                              Flex harUI Adobe Employee

                                              What version of the Flex SDK are you using and what version of FB and FlashPlayer?  You might be missing information if the SDK and FB and FP don’t match up.

                                               

                                              Line numbers don’t match up exactly for me, so I’m assuming the UIComponent lines are the self-referencing event listeners in the constructor.

                                              The last function (Capture12: GroupBase: set layout) causes the layout to have a reference to the skin.  So next you need to see if anything is hanging onto the layout.

                                              • 20. Re: Looking for memory leaks
                                                Zolotoj Level 3

                                                I am using SDK 4.5.1, FB 4.7, and FP 11.6.

                                                 

                                                < I’m assuming the UIComponent lines are the self-referencing event listeners in the constructor.

                                                Can you elaborate on that, please?

                                                 

                                                <The last function (Capture12: GroupBase: set layout) causes the layout to have a reference to the skin.  So next you need to see if anything is hanging onto the layout.

                                                Not sure I understand. Where would I look and what I suppose to look for?

                                                 

                                                Thanks for help.

                                                • 21. Re: Looking for memory leaks
                                                  Flex harUI Adobe Employee

                                                  Go to flashplayerversion.com and see what it says about your player version.

                                                   

                                                  FB4.7’s profiler was probably tested with FP11.2 or FP11.3.  There is a chance that FP11.5 doesn’t report samples in a way that FB can handle.

                                                   

                                                  UIComponent’s constructor calls addEventListener on itself.  That can’t cause a leak.

                                                   

                                                  There is some layout instance assigned to those renderer instances.  They should also be in the profiler’s snapshot and you should check to see if they are getting stuck in memory somehow.  Use the debugger to see what the layout class is if you’re not sure.

                                                  • 22. Re: Looking for memory leaks
                                                    Zolotoj Level 3

                                                    Tried 11.2. Same.

                                                     

                                                    But, call me crazy, but I just dont see how someone who is not a Flex engineer can get from this info to actual code that supposely creates a memory leak in a first place:

                                                    part 1:

                                                    Capture14.PNG

                                                    Part 2

                                                    Capture15.PNG

                                                    Part 3:

                                                    Capture16.PNG

                                                     

                                                    Here is code for one of these renderers that are using the skin:

                                                    package .....view.component.navigationmenu.renderer

                                                    {

                                                              import spark.components.Button;

                                                     

                                                              import .....view.component.navigationmenu.renderer.skin.DefaultToggleButtonRendererSkin;

                                                     

                                                     

                                                              public class DefaultTopLevelButtonRenderer extends ButtonRendererBase

                                                              {

                                                                        public function DefaultTopLevelButtonRenderer()

                                                                        {

                                                                                  super();

                                                     

                                                     

                                                                                  setStyle('skinClass', skinClass);

                                                                        }

                                                                        override protected function get skinClass():Class{

                                                                                  return DefaultToggleButtonRendererSkin;

                                                                        }

                                                              }

                                                    }

                                                     

                                                    ButtonRendererBase

                                                    package .....view.component.navigationmenu.renderer

                                                    {

                                                              import flash.events.MouseEvent;

                                                              import flash.events.TimerEvent;

                                                              import flash.utils.Timer;

                                                     

                                                              import flashx.textLayout.formats.TextAlign;

                                                     

                                                              import mx.controls.Image;

                                                              import mx.events.ResizeEvent;

                                                              import mx.managers.ToolTipManager;

                                                     

                                                              import spark.components.Button;

                                                              import spark.components.Group;

                                                              import spark.components.Label;

                                                              import spark.components.ToggleButton;

                                                              import spark.effects.Resize;

                                                              import spark.primitives.BitmapImage;

                                                     

                                                              import ....view.component.navigationmenu.NavigationMenu;

                                                     

                                                     

                                                              [Style(name="bgColor1",                     type="uint")]

                                                              [Style(name="bgColor2",                     type="uint")]

                                                              [Style(name="textAlignment",           type="String")]

                                                              [Style(name="colorRollOver",           type="uint")]

                                                              [Style(name="showArrow",                     type="Boolean", enumeration="true, false")]

                                                              [Style(name="collapsible",                    type="Boolean", enumeration="true, false")]

                                                              [Style(name="rotateChevron",          type="Boolean", enumeration="true, false")]

                                                     

                                                              public class ButtonRendererBase extends ToggleButton

                                                              {

                                                                        [SkinPart(required="false")]

                                                                        public var iconDisplayed:BitmapImage;

                                                     

                                                     

                                                                        private var _iconLocation:String;

                                                                        private static const DEFAULT_ICON_LOCATION:String = "asset/flex_logo.jpg";

                                                                        private var _menu:NavigationMenu;

                                                     

                                                                        protected var bgColor:uint;

                                                     

                                                     

                                                                        public function ButtonRendererBase()

                                                                        {

                                                                                  super();

                                                                                    this.setStyle('textAlign', TextAlign.LEFT);

                                                                                  }

                                                                        protected function get skinClass():Class{

                                                                                  //abstract

                                                                                  throw new Error('Must override skinClass getter in derived component');

                                                                        }

                                                     

                                                     

                                                                        public function set iconLocation(val:String):void

                                                                        {

                                                                                  _iconLocation=val;

                                                                                  if (iconDisplayed)

                                                                                  {

                                                                                            iconDisplayed.source=val;

                                                                                  }

                                                                                  invalidateDisplayList();

                                                                        }

                                                     

                                                     

                                                                        public function get iconLocation():String

                                                                        {

                                                                                  return _iconLocation ? _iconLocation : "";

                                                                        }

                                                     

                                                                        public function set menu(val:NavigationMenu):void{

                                                                                  _menu = val;

                                                                                  invalidateDisplayList();

                                                                        }

                                                     

                                                     

                                                                        override protected function childrenCreated():void

                                                                        {

                                                                                  super.childrenCreated();

                                                                                  if (iconDisplayed && _iconLocation)

                                                                                  {

                                                                                            iconDisplayed.source=_iconLocation;

                                                                                  }

                                                                                  addEventListener(MouseEvent.MOUSE_OVER, onMouseOver);

                                                                        }

                                                     

                                                                        public function getWidth():int

                                                                        {

                                                                                  return iconDisplayed.x + iconDisplayed.width + Label(labelDisplay).width + Label(labelDisplay).right + (Group(this.skin).getStyle('horizontalGap') * 4);

                                                                        }

                                                     

                                                                        private function onMouseOver(e:MouseEvent):void{

                                                                                  var combinedWidth:Number=Label(labelDisplay).width + iconDisplayed.width;

                                                                                  if (combinedWidth > this.owner.width)

                                                                                  {

                                                                                            this.toolTip = this.label;

                                                                                  }else{

                                                                                            this.toolTip = null;

                                                                                  }

                                                                        }

                                                     

                                                                        override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void

                                                                        {

                                                                                  super.updateDisplayList(unscaledWidth, unscaledHeight);

                                                                                  Label(labelDisplay).visible=true;

                                                                                  var combinedWidth:Number=Label(labelDisplay).width + iconDisplayed.width;

                                                                                  //added null check to get around the remove policy tab cause error issue

                                                                                  //TO DO:  need to figure out why that is the case.

                                                                                  if (this.owner){

                                                                                            if (combinedWidth > this.owner.width)

                                                                                            {

                                                                                                      this.width=iconDisplayed.width + 10;

                                                                                                      Label(labelDisplay).visible=false;

                                                                                            }

                                                                                            else

                                                                                            {

                                                                                                      this.percentWidth=100;

                                                                                                      Label(labelDisplay).visible=true;

                                                                                            }

                                                                                  }

                                                                        }

                                                              }

                                                    }

                                                    • 23. Re: Looking for memory leaks
                                                      Flex harUI Adobe Employee

                                                      Finding memory leaks can be very hard.  Back in the day, I’d say I got interrupted every 3 months or so by a major Adobe customer who escalated an issue through their support contract that required my assistance working with their code and the profiler.  Unfortunately, I’ve never been able to capture my methodology other than what I’ve told you so far.  A lot of it becomes “feel” where by looking at the source code and the profiler output I start to eliminate possibilities until I find out what is going on.

                                                       

                                                      This most recent scenario about the renderer’s skin and its layout is making me wonder if you really have a leak here.  It would probably require a custom layout that is misbehaving.  There could be other reasons why instance counts are going up.  If you resize a list or scroll it, it might create more cached instances (although then the instances should show that the cache is pointing to it).

                                                       

                                                      Basically you have to understand how your code works and try to get the Profiler to help you analyze it.  Sometimes there are very good reasons for instance counts to go up.