`
bk562bk
  • 浏览: 26652 次
社区版块
存档分类
最新评论

Flex Memory Management and Memory Leaks (zz)

 
阅读更多

  You probably heard, read or even learned that Flex was managing the memory automatically for you, does it mean you don't have any responsibility regarding memory management in Flex? Absolutely not! If you're surprised by my answer, keep reading to understand how memory is managed in Flex using garbage collection, what responsibilities this is putting on developers, what the classical causes of memory leaks are and what are the good practices to enhance or optimize the memory management in your applications. Let's start by clarifying how Flex manages the memory before discussing common best practices regarding memory management in Flex applications . The Flash Player is responsible for providing memory for your flex application at runtime. The memory that the Flash Player provides to Flex applications must be requested to the computer's operating system. Knowing that OS memory allocation is a slow operation, the Flash Player is using a buffering technique to avoid requesting frequent allocations. This technique consists in managing two different types of memory: the OS memory1 and the Application memory. The OS memory is requested by the Flash Player in big chunks for performance reasons and kept as a memory pool in which Flex objects can be allocated. At startup, the Flash Player initializes the Application memory pool. Then, as the application creates objects, the pool is used to provide memory for the new objects. In case the pool wouldn't have enough available memory for a new object, the Flash Player requests a new big chunk to the operating system. 
  
  When your Flex application deletes some objects, the Flash Player doesn't release the corresponding OS memory but just makes it available for any further allocation (i.e. the memory is back in the Application pool). The OS memory is released only if a complete big chunk is not used anymore by your application. 
  
  To conclude on the Flex Memory Allocation topic, it must be mentioned that in Flex, you cannot explicitly delete an object (or free a chunk of memory). Object deletion, and related memory release operation, is automatically managed by the Flash Player itself, using a particular mechanism called Garbage Collection2 . Creating objects in Flex is an explicit operation that you trigger by calling the new operator. On the other hand, deleting an object is not an explicit operation (i.e. there is no delete operator3 . It's the Flash Player itself that is responsible to check which objects are useless and delete them. This mechanism is called Garbage Collection. Basically, a useless object is an object that is not anymore referenced by another active object (i.e. orphan objects). Identification of these orphan objects is a complex task and we'll see later how you, as a developer, can help the Flash Player to make it efficient . The global Garbage Collection process can be seen as a two steps process. The first step being to identify the useless objects; and the second simply consisting in releasing the corresponding memory. As we saw when detailing Flex Memory Allocation principles , the memory retrieved when a useless object is deleted becomes available for a future allocation or, in some cases, can be released at the operating system level. The first step, usually known as "Marking objects for deletion", is implemented using the two following procedures: Reference counting is one of the simplest methods for keeping track of active references. When a strong reference to an object is created, the object reference count is incremented. When a strong reference to an object is removed, the object reference count is decremented. If the reference count of an object reaches zero, it is marked for deletion. This is simple, fast and efficient but limited since it doesn't work for circular references (cross references between orphan objects). This is why the Flash Player supports a second garbage collection procedure called Mark and Sweep. Note: References can also be Weak References . This second garbage collection procedure works in a reversed way compared to the Reference Counting since it relies on identifying objects that are still accessible somewhere in the object tree of the application. The Flash Player starts at the root node of the application, and goes through every reference on it, marking each object it finds. It then iterates through each of the marked objects, marking their children. It continues this recursively until it has traversed the entire object tree of the application, marking everything it finds. At the end of the process, if an object in memory has not been marked, it means that no active reference to the object was found. So, such objects can be marked for deletion. The second step of the Flex Garbage Collection process consists in deleting the objects that have been marked for deletion. It is key to understand that this second step is a deferred operation. This means that objects are not deleted immediately when all active references are deleted. The deletion phase may happen at some "indeterminate" time in the future (yes, I said may  because there are cases in which the deletion phase will never be executed). The Flash Player uses a set of heuristics that look at RAM allocation, the size of the memory stack and different other criteria to determine when to trigger the deletion. This is very important since it means that your objects, even when not referenced anymore, may continue to live for some time. It is then key to make such objects inactive so that they do not keep consuming CPU before they really be deleted (marked for deletion objects still receive events for example; the only difference compared to normal objects is that they won't be rendered). There is no way to force garbage collection for a production application. System.gc() is available but enabled only when the application runs in debug mode. Anyway, when debugging applications, it may in some cases be useful to explicitly trigger the garbage collection. In such a case, you must know that calling System.gc() does not trigger the garbage collection in a synchronous way but queues the GC request such that it be executed on the next frame. You'll often see people calling System.gc() twice in a row and explaining that the first call is for garbage marking and the second for garbage sweeping but, to my understanding, it does not work except if you wait for the next frame to call System.gc() again. In addition to being an automatic process, there is no guarantee that all Garbage Collection steps be completed in one run. Most of the time, the Flash Player will execute the Garbage Collection in several incremental steps. The last key characteristics of the Flex Garbage Collection process is that it is triggered only when allocating . This means that memory is not released when it not used anymore, but, instead, when more memory is requested by the application. Note:Grant Skinner created a Garbage Collector Interactive Simulator that nicely illustrates these principles. Now that we discussed how memory is managed in Flex using garbage collection , let's see what developers can do to help the garbage collection process and to limit memory leaks in their applications. The first easy way to reduce memory issues is just to allocate objects only when needed. This means deferring object allocation until really necessary (in createChildren() for example). This may seem obvious but is not always done correctly since the Flex Component Life Cycle remains a mystery for some of us. The second good way of reducing memory consumption is by recycling objects. Instead of releasing an old object and creating a new one, it is more efficient trying to reuse the already allocated object by resetting its properties. This can be done on a case by case basis for limited recycling or by implementing pools of reusable objects when manipulating a big number of instances. The Flex framework implements such a pool concept for item renderers by recycling them when scrolling a List or a DataGrid instead of allocating one renderer for any item in the container. Whenever an object is released, you should ensure that it is removing all references it holds on other objects. We'll see below what should be released and how but let's first highlight that the complex part here is often determining when an object is released. What I mean is that there is no destructor available in Flex, so a given object has no direct way of knowing that it has been released. In consequence, when developing a custom component, it is not always possible to ensure that releasing it is also releasing all referenced objects. To workaround this, a very common approach is to implement (and document) a clean up interface that developers using your custom component must call when they want to release an instance of your custom component. Such an interface usually offers a single method, dispose() or destroy() , in which you implement all the necessary clean up: stop timers , remove event listeners , unload loader objects , set variable references to null , … It's then up to developers using your component to call the dispose() method just before releasing your object. Event listeners are probably the most common cause of memory leaks in Flex applications. Adding an event listener creates a reference to the object holding the handler that may prevent the garbage collection until it is removed (i.e. the dispatcher references the listener). You should always remove event listeners that are no longer needed. For events that are fired only once, remove the event listener in the handler associated to the event: For events that you must process regularly during the life of your component (such as Timers, or mouse events), clean them up in your dispose() method. Note:Listeners on child objects generally do not cause memory leaks but it is anyway a good practice to remove them explicitly. When calling addEventListener() , you can force the reference that will be created on your callback to be a weak reference (by setting the fifth parameter of addEventListener() to true). Weak references are not counted as strong references are. It means that an object that has no remaining strong reference to it but only weak references will be marked for deletion. Many of us are complaining that the weakRef parameter should have true as its default value and it's quite common considering always using weak references as a good practice; anyway, using weak references is not without potentially nasty consequences. First of all, you should never use weak reference for an anonymous handler: In this example, the only reference to the function is a weak reference, meaning that next time the garbage collection runs, the function will be removed, and so, not be called anymore. Anyway, I really consider that using anonymous functions is not recommended. Now, even when using weak references for your event listeners, you should explicitly clean them up when they are no longer needed (remember that the object removal is not predictable). Note:Weak references are also supported in the Dictionary object. To use weak references in a dictionary, just set the first parameter to true when calling the dictionary constructor. Event.ADDED_TO_STAGE is only dispatched when a Display Object is added to the Display List and all its parents are also in the Display List4 . Event.REMOVED_FROM_STAGE is dispatched when an object or any of its parents is removed from the Display List5 . These events can be used to optimize the management of event listeners. When the object is on the stage, ensure that it listens to events, when it is not, stop listening to events (except for Event.ADDED_TO_STAGE of course). I initially thought that Event.REMOVED_FROM_STAGE could be considered as a destructor but this is not the case! When an object is re-parented (moved from A to B), it will receive an Event.REMOVED_FROM_STAGE (when removed from A) followed by an Event.ADDED_TO_STAGE (when added as a child of B). Note:This may also happen when objects are part of a container supporting scrollbars. When the container is resized, scrollbars may appear or disappear. In such a case, all children of the container may be re-parented in/out an internal content pane created/deleted by the resized container.  Whenever using an object based on a loader (Image , SWFLoader , …), you should always call the unloadAndStop() method to unload the content from the loader and help the garbage collection. When releasing an object, you should clean up references that it was holding in variables. Flex developers are not responsible of allocating and freeing memory since this is done by the Flex framework using a Garbage Collection mechanism. But Flex developers are anyway responsible of helping the garbage collection by carefully managing data allocation and references clean up. Managing references efficiently and identifying memory leaks is not an easy task. Fortunately, some tools are available to help us among which I strongly recommend the Flex Profiler provided with Flex Builder. 
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics