flex 内存回收经典文章(1)

     文章收集:

    http://spreadingfunkyness.com/garbage-collection-with-flex-and-adobe-air/

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.

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?

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.

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

你可能感兴趣的:(UI,Flex,Flash,Adobe,AIR)