I finally found some spare time to organize the stuff presented at flexcamp and make it a blog post. I “argue” with the Flex profiler almost daily and we had an “intense” relationship the month right before the flexcamp. So I felt a talk about profiling and Garbage Collection (GC) was really fit. Right, it is impossible to decouple profiling from GC. If you want to improve the memory management of your application you have to know how the Flash Player (and Adobe Air) manage memory allocation and deallocation.
Let me start by saying there are many blog posts about issues related to GC and profiling, ranging from documentation, presentations, how-tos, etc. I list them at the bottom.
The present post can be considered useful to beginners and medium experienced people which use Flex almost daily and have the need to optimize memory consumption. After introducing the fundamental concepts I will list a set of lessons learned during the development of Posty and Focused. I will particularly focus on the use of renderers’ caches.

Virtual Machine (VM)

The flash player is based on a virtual machine (to be precise the machines are 2, one for actionscript2 and one for actionscript3). VMs dynamically allocate memory when you create new objects. For example the following line of code creates a new Object.

1
var o:Object = new Object()

At startup the VM reserves some memory and when the code above is executed decides where the object goes in the application memory and how much space it takes. As you create objects the VM might use all the memory allocated at startup and, when needed, requests some more to the operative system. Unless you have a program which nonsensically just creates new objects, there will be some time during the execution when an object becomes “useless”, e.g. it is not needed anymore for the correct execution of the program. You’ll see that it is not easy to “understand” when an object is not needed. For now let’s assume we are able to detect it.
In the Flash VM achitecture you cannot explicitly say “delete it”. Memory usage is managed, that is the VM itself is responsible to check which objects are useless and delete them. Such a mechanism is called Garbage Collection.

Garbage Collection

So what can I do as a programmer? Well, you can ease the task of the garbage collector. Let’s see what we expect to happen with the help of a figure.

Memory Blocks Example

At startup the application reserves some memory to be used, say four blocks. When you create a new object the VM allocates it using the first slot. Let’s say that after a while o1 is not needed anymore and you set it to null. When you create a new object, o2, you expect that it takes the place of o1. Sometimes it happens, sometimes it does not. This depends on the garbage collection mechanism, which is a pretty complex procedure we are not describing here (a good article has been written by Grant Skinner).
At this point we already have a lesson learned: “setting an object to null does not necessarily free the memory it was occupying“.

This depends on the way GC has been implemented in flash. GC is triggered by allocation” and not deletion. This means that the GC cycle is run when you say new Object() and not when you set it to null.

Memory Consumption

If you have to do with AS3 and Flex you probably know that you can dynamically add UI elements to the graphical interface via a simple method called addChild(). The opposite method is removeChild(), which removes a display element from the UI. To be more precise, the element is deleted from the view (it is not displayed anymore) but this does not mean it has been garbage collected. Let me introduce you to a simple scenario to show you easy is to put too much trust in the removeChild() method.
Many Flex applications load data from a server and display it dynamically, according to the values returned. Usually the view code is isolated in a component, often referred to as renderer, which is responsible of showing the data loaded from the server. We devise a very simple renderer that is made of two text fields, embedded in a VBox. Data shown are field1 and field2, properties of the object provided as input.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" 
    borderStyle="solid" width="200" 
    >
    <mx:Script>
        <![CDATA[
	    [Bindable] private var _field1:String;
	    [Bindable] private var _field2:String;
 
	    override public function set data(value:Object):void {
		_field1 = value.field1;
		_field2 = value.field2;
	    }
	]]>
	</mx:Script>
 
	<mx:Text text="{_field1}" />
	<mx:Text text="{_field2}" />
</mx:VBox>

Let’s simulate data loading by means of a simple function, which returns an array of objects.

1
2
3
4
5
6
7
8
9
10
11
private function getData():Array {
    var a:Array = new Array();
 
    for (var i:uint = 0; i< 200; i++) {
	var o:Object = new Object();
	o.field1 = "field "+Math.random().toString();
	o.field2 = "field "+Math.random().toString();
	a.push(o);
    }			
    return a;
}

To render data we use a simple function which creates a renderer, sets its data, and adds it to the VBox.

1
2
3
4
5
6
7
8
9
10
11
private function loadData():void {
    vbox.removeAllChildren();
    var array:Array = getData();
 
    for (var i:int = 0; i < array.length; i++) {
	var rend:MyRenderer = new MyRenderer();
	rend.data = array[i];
	box.addChild(rend);
        i++;
    } 
}

Do you see the dangerous statement? Not yet? Let me show you. To simulate a repeated action I add a timer which calls the load function each N seconds.

1
2
3
4
5
private function init():void {
    var t:Timer = new Timer(2000);
    t.addEventListener(TimerEvent.TIMER, tick);
    t.start();
}

Now try running the profiler. Do you see an ever growing graph like this?

Memory Leak

Congrats! We have found a memory leak! A memory leak happens when the same action is repeated and memory consumption grows instead of being constant. Did you find the dangerous statement? It is when you create the renderer. Why? Because you assume that removeAllChildren() removes renderers from the memory. Wrong! As said above that method only removes renderers from the display tree. In fact, as you can see in the profiler, renderers are still there, consuming memory.

UPDATE: Technically speaking this is not a memory leak, because there is nothing that prevents the garbage collector to clean up memory from renderers. A memory leak happens, in fact, when there is something that is supposed to use the renderer (e.g. a listener) even when you remove it from the display tree. In this example renderers are ‘free” and could be garbage collected. But they are not. So the result is the same of a memory leak, an ever growing memory consumption.

Instances of renderers

There are many techniques to solve this situation. We will show two: caching renderers and dynamic caching.

Caching

Let’s assume you know in advance how many renderers are needed. One way to solve the memory leak is to create a cache of renderers at startup.

1
2
3
4
5
6
7
private var cache:Array = new Array();
 
private function initRenderers():void {
    for (var i:int = 0; i < 200; i++) {
	renderers.push(new MyRenderer());
    }
}

We can then modify our loadData method like this:

1
2
3
4
5
6
7
8
9
10
11
private function loadData():void {
    container.removeAllChildren();
    var array:Array = getData();
 
    for (var i:int = 0; i < array.length; i++) {
	var rend:MyRenderer = cache[i];
	rend.data = array[i];
  	container.addChild(rend);
	i++;
    } 
}

As you can see in the code we don’t create new renderers but we look for one in the cache. There are cases when you don’t know in advance which data are returned from the server and then you don’t know how many rendereres you need. In this case you need a dynamic cache.

Dynamic cache

Dynamic caching is based on an elastic mechanism. You have a place where you can look for a renderer: if there is one in the cache, that is returned, otherwise a new one is created temporarily. Better to see some code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class MyRendererCache extends Object {
 
    private static var cache : ArrayCollection = new ArrayCollection();
 
	public function MyRendererCache() {
	    super();
            for ( var x : Number = 0; x < 20; x ++ ) {
		cache.addItem( new MyRenderer() );
	    }
	}
 
	public static function getRenderer() : MyRenderer {
	    var renderer : MyRenderer;
 
	    if ( cache.length <= 0 ) {
		renderer = new MyRenderer();
	    } else { 
		renderer = cache.removeItemAt( 0 ) as MyRenderer;
	    }
 
	    return renderer;
        }
 
        public static function setRenderer( renderer : MyRenderer ) : void {
	    cache.addItem( renderer );
        }		
}

In the constructor you populate the cache with the minimu number of renderers, say 20 (lines 7-9). The cache has two static methods, getRenderer and setRenderer. The first is used to obtain a renderer, the second to give it back when done. If you look and lines 15-16 the cache returns a new renderer when the cache is empty. This way the number of renderers in memory can grow beyond the minimum number set in the constructor, but just temporarily, since the GC will delete them when not referenced anymore.
An important issue is related to the setRenderer. When you don’t need a renderer anymore you have to return it back to the cache, otherwise we fall again in a memory leak as explained above. To achieve this we exploit the remove event of the renderer. The remove event is triggerer whenever a UI element is removed from the display list. For example when we call removeAllChildren() such event is triggered for each renderer.
We can modify the renderer like this:

1
2
3
4
5
6
7
8
9
10
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" 
    borderStyle="solid" width="200" 
    remove="onRemove()"
    >
 
    private function onRemove():void {
	MyRendererCache.setRenderer(this);
    }
....
</VBox>

If you now run the profiler you will notice that memory grows until a given point and then keeps stable as in the figure below.

Stable Memory Consumption

Congratulations! You have solved the memory leak!

Suggesting the GC

Besides favoring the job of GC Adobe has allowed programmers to suggest the intervention of the GC. The command is in the flash.system package and it is System.gc(). By documentation this “Forces the garbage collection process” but in my experience it is just a vague suggestion of intervention. It can solve some situation, so it is worth trying it at the beginning, when you need a quick way to save some memory.


Here are the slides of my talk at flexcamp.




Full screen slides available here.

The source code of this post is available under the new BSD license.

References

Similar Posts

  • Share/Bookmark

Comments

  1. I wonder when Adobe will finally give us decent tools to analyze memory leaks.
    I’m speaking of functionality like those describe at
    http://kohlerm.blogspot.com/search/label/memory

    January 12, 2009
  2. @markus let’s hope in the new version of Flex and Flex builder. I am tempted to try also other IDEs, to see whether they provide some more support. I heard of IntelliJ Idea, Visual studio can integrate Flex development capabilities. Will try to find some time to test them. Of course I will blog about that :)

    January 12, 2009
  3. You should call ‘new ArrayCollection’ in the constructor of MyRendererCache.

    January 14, 2009
    - Vladimir Angelov
  4. @vladimir Fixed. I initialized the array outside of the constructor. Thanks for reporting.

    January 14, 2009
  5. Can you clarify, if removeAllChildren() isn’t enough to cause garbage collection for the renderer, then what reference exists that prevents the GC from clearing it?

    January 14, 2009
  6. Probably I wasn’t precise in the explanation. I’d rephrase and say:

    “removeAllChildren() isn’t enough to cause garbage collection IMMEDIATELY”

    If you take a memory snapshot and try looking at instances of MyRenderer there is no reference left back. Still you see the curve growing. This is due to the fact that the detection of unneeded objects is very expensive (in terms of CPU) and it is run occasionally.
    My example is really demanding in terms of new instances of MyRenderer (200 every 2 seconds) and such a request hogs the CPU and prevents successful detection of renderers to be garbaged.

    Hope now it is more clear.

    Thanks for your question.

    ps: I ran the profiler for 10 minutes. Instances went up to 4300 and GC didn’t intervene.

    January 17, 2009
  7. I think your use of the term “memory leak” is a bit sensational. Your example with removeAllChildren is not an example of a leak. A leak is when some memory is claimed but never returned. The memory used by the renderers in your example is going to be returned as soon as the runtime decides that it’s time to run the garbage collector. If you had pressed the GC button in the profiler just after taking the screenshot of the memory graph you would have seen it drop.

    It may be inefficient to create and throw away UI objects in the way you describe, but it is most certainly not an example of a leak.

    To create a real leak you can add an event listener to the objects when they are created, but not remove it, that way your code would leak for real.

    Your optimization suggestions are good enough to stand on their own, without the scary story about memory leaks.

    February 16, 2009
  8. You are right, my code does not reproduce a leak, because GC will in the future release that memory, whereas the definition of leak implies that memory is NEVER released.
    What I want to point out it that my code generates an application which consumes memory AS IF there were a memory leak, because memory is never released actually, but just in principle (or when you click GC in the profiler). In fact, the VM is too busy doing other stuff and sort of *forgets* to run GC.

    To sum up I agree with you, technically this is not a memory leak, but it produces an application which is not useable and behaves most of the time as a leaky one.

    February 26, 2009
  9. Hey Funkyboy,
    If you agree that “technically this is not a memory leak”.
    You need to update your blog and replace the words “memory leak” with “memory optimization otherwise you are misleading people.

    March 13, 2009
    - Vijay
  10. @Vijay I updated the post to explain that technically speaking this is not a memory leak. Thanks for the feedback.

    April 17, 2009
  11. I’m wondering how you would implement such an optimization when using a list or a datagrid class. i ask because you don’t specify an object as the itemRenderer, you specify a class , ie:

    [Bindable] private var listData:ArrayCollection;

    I’m finding some serious memory issues when changing the dataProvider, ie

    listData = new ArrayCollection([1,2,3,4]);

    In my casse the itemRenderer is also displaying an image which makes the memory consumption start to become a big issue.

    Any thoughts?

    May 15, 2009
  12. @Christoff First I should say that there are Flex components (Canvas, Scrollbar) and there is DataGrid. Many hate Datagrid (one for all Doug http://dougmccune.com/blog/2009/02/08/analyzing-the-size-of-the-flex-framework-or-why-i-hate-the-advanceddatagrid/) and many suggest to not use it unless you really cannot do otherwise (Sean’s 24th tip http://www.insideria.com/2009/04/51-actionscript-30-and-flex-op.html).

    The principle is the same: get a bunch of itemRenderers at startup, store them in a collection and try to reuse those across your app. If they are not enough you can create new ones.
    A more advanced technique (probably the most advanced) is to use only the renderers which are visible in a Datagrid and to add new ones (while removieng old ones) only when you scroll the grid. BTW this is the technique adopted by cell renderers in the iPhone sdk.

    May 15, 2009
  13. I am trying to implement your dynamic cache and it complains that “MyRenderer” is not a compile-time constant. My first attempt to optimize my app using your technique

    May 29, 2009
    - Ben
  14. @ben If you like you can post me the code and I’ll try to have a look at it.
    You can also use my code as a starting point (link at the end of the post).

    May 30, 2009
  15. Thank you very much!
    This is of great help to me,i fixed my EshopDesktop Application debug!
    Thank you!

    August 21, 2009
  16. For what its worth, System.GC() is only operational for debugger players (not the release/standard Flash Player). Also worth noting that System.GC() is asynchronous and only a request to the player to run through a mark/sweep pass on the next frame interval.

    January 21, 2010
  17. @Markus, do you have any specifics as to why you feel the profiler in Flex Builder 3 (or the newest profiler in the pre-release version of Flash Builder 4) is not a “decent tool to analyze memory leaks” ?

    January 21, 2010
  18. @funkyboy, Keep in mind that the way the profiler works, is that it’s playing back samples that it receives from the running player over time. If you are rapidly creating instances then freeing them up, the profiler sometimes takes a bit to “catch” up to “real time” as its processing and analyzing the samples its receiving (tons of data to wade through).

    The best way to see how quickly the GC is accounting for clean up is to use the new 10.1 player (which is now in beta).

    There is a new property called System.privateMemory which gives a much better accounting of *all* the memory in use by the player process – best to try testing with a standalone player if you can get ahold of one.

    But then just have your app poll System.privateMemory (or System.totalMemory if using the release 10.0 player) every frame and you should see a much more rapid drop than you do when you are actively profiling.

    January 21, 2010
  19. @Corey Thanks for stopping by. I will have a look at the new 10.1 player and see if it optimizes memory usage. When will it be released as final?

    January 21, 2010
  20. In your caching example, the cache grows but never shrinks. In the case of a screen where you are loading data from the server, the cache may spike to display the contents of the longest collection. However, if you then load a set of data that is 10% of the previous one, your cache would still have far too many renderers cached. Is there a preferred technique for destroying unneeded renderers in a way that frees their memory?

    January 21, 2010
  21. Now that I think of it, wouldn’t the fact that you are keeping a reference to extra renderers in your cache prevent them from ever being garbage collected? Where in the normal case they do eventually get collected?

    January 21, 2010
  22. @Rob Thanks for your question. This depends on the policy of the cache setter. Instead of just adding the “used” renderer to the cache you can do a check on the length of the cache itself and, if greater than a given threshold, you can avoid to add it. To me, the only way to find the “best” value of the threshold is by experimentation, for it depends on many factors: the application, the number/frequency of queries, the number of returned results, the complexity of the renderer…

    January 21, 2010
  23. @Rob The cache prevents them from being collected, but is also establishes a limit: if you loaded 20-30-10 items your cache length will be 30. With no cache you might have, in the worst case, 60 renderers.
    If you have an application with high variability (alternate loading of many and few data) it is better to have a policy in the setter, as explained above.
    In the “normal case” it depends on the implementation: e.g. if you use a Repeater component you can have a look at the generated code, if you code it your own you are responsible for that. My solution is just a way to have a controllable “centralized” mechanism to generate/delete renderers, so if there are issues about memory I know where to look in the code.

    January 21, 2010
  24. Understood… the way I have implemented this in Klok is that the container manages the renderers by doing basically what your cache does so that when the data provider changes, no new renderers are created unless there weren’t enough. However, if the new data provider has less items, then I remove the child renderers using removeChild. I would like to ensure some way of efficiently freeing their memory so that I don’t have the problem you describe.

    January 21, 2010
  25. Beware, removeChild just removes a renderer from the display tree, as explained in the post. This does not ensure it is removed from memory. Before calling removeChild you can call MyRendererCache.setRenderer(…) to give it back and there you can check whether your cache is too big or not: if yes, you can remove some renderer also from the cache.

    January 21, 2010
  26. So when removing from the cache, how do you ensure that its memory gets freed? It doesn’t look like it is doing anything special with the renderers other than store them in a collection. Would cache.removeItemAt(n) free the memory allocated to the renderer and position n? I guess I don’t understand why removing an item from the cache would behave any differently than removing from the display list (assuming there are no other references to the renderer).

    January 21, 2010
  27. The cache I use is a way to ensure that each renderer has only, at most, 2 references: one in the display tree and one in the cache itself. I got to this solution by experimentation and I solved many of the issues I had when dealing with renderers.

    January 21, 2010
  28. great post,I’ve translate to chinese:
    http://www.riameeting.com/node/587

    January 24, 2010
  29. Leave a Reply