If I want to profile an app for memory leaks what is the best approach?
Thanks
I am following your presentation and want to show two images:
So, let's say I have the following entry in Loitering Objects:
Double clicking on it will show this:
Where do I go from here?
Also I took this snapshot without touching the app. and compared it to second snapshot:
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:
Clicking on it will show the following in the Allocation Trace:
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
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
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.
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.
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.newStatusCountRangeC hangedEvent(statCntType,rangeType));
}
}
}
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.
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.
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.
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:
Part 2
Part 3:
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.DefaultToggleButtonR endererSkin;
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;
}
}
}
}
}
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.
North America
Europe, Middle East and Africa
Asia Pacific