I am currently working on a large Flex application. It is structured around a document metaphor. Documents contain pages, and pages contain reports. Reports are tiled on the page, and generally contain Flex charts. The user can drag new reports on to the page, move reports around on the page, create/delete pages, and toggle between pages. It's relatively complex, and there are a lot of moving parts.
I noticed that as I tested the application for longer periods of time, it began to slow down. I look at Internet Explorer's memory utilization while running the application. It seemed rather high. As Flex applications can be very heavy weight, this was not necessarily a concern. What was alarming was the fact that as I tabbed back and forth between pages in a document, the memory utilization kept rising. I hit 500MB, 600MB, 700MB... the sky seemed to be the limit.
I had seen the new profiling tools in Flex Builder 3 at Adobe Max this year, so I downloaded FB3 on my development box and ran the application with the profiling tools. One of the tools is a live memory watcher. It will show you the maximum and current instance count of every object in the system, along with their memory footprints.
At first there was nothing obvious, but as I ran the application longer, an object called “ReportPod” bubbled up to the top, with an ever increasing instance count and a massive memory footprint. ReportPod is a container object that supplies a bit of UI chrome around the previously mentioned charts. At any given time there are at most 10 of these objects being displayed to the user. As a user tabs between pages. the previous page's ReportPod will be removed from the display list and ReportPods for the new page will be created. ReportPods are constantly being created, added to the display list, and then eventually remove from the display list. But it was clear that after they were removed from the display list, they were not being garbage collected as they should be.
Takin' the garbage out
The Flash garbage collector works by counting references. To simplify a bit, if an object is not referenced by any other object on the display list, it will be garbage collected. If however, the GC can reach the object via a reference chain, however circuitous, the object will not be garbage collected. Even a single reference to an object can keep it alive, even after it itself is removed from the display list. A typical cause of this problem can be event listeners. Event listeners create “hidden” object references. For example:
child.addEventListener(MouseEvent.CLICK,clickHandler(event))
This will create a reference from child, to its parent. This isn't really an issue, as “islands” of objects that only have references to themselves are easily garbage collected. But something like the following can cause a memory leak:
parent.addEventListener('someEvent;,eventHandler(event))
If this event handler is not remove by calling “removeEventListener”, the parent object will retain a reference to the listening object, in order to be able to call its event handler function. The listening object will not be garbage collected, even after it is remove as a child object from the parent. Such situations are relatively rare, and in general, if all you are doing is listening for events on child objects, you don't have to clean up and remove your listeners, but in any other situation, it's good practice to make sure you remove your event listeners before the object is removed from the display list.
I looked for event listener leaks in my ReportPod, and it's child components, and couldn't find anything obvious. We'd actually gone to quite a bit of trouble to clean up stray listeners, even when we didn't have to. After several hours of pulling my hair out, I decided to take advantage of a new feature in Flex 2 (and I believe the Flash 9 player). In Flex 2, you can “reparent” visual components. You can remove a component from one parent container, and add it to another.
If you can't beat 'em, join 'em
As my ReportPods were not being garbage collected, I decided to create an object pool, a reusable collection of ReportPods, instead of always creating new ReportPods. When the user tabbed from one page to another, the previous page's ReportPods would be removed from their container, and returned to the pool, and then reused, and reparented as the new page was created. By reusing them, I controlled the number of instances, and as each page had a maximum of ten reports, there would only ever be ten ReportPod instances total. It wouldn't matter if they were never garbage collected. The application would us a bit more memory than it should, but memory utilization would not increase without bound.
After dealing with all of the issue involved in initializing and de-intializing pooled ReportPods, I had the application back up and running. This looked promising at first. Indeed I now had control over the number of instances of ReportPod. But it quickly became obvious that ReportPod had not been the cause, but rather a child of the ReportPod, called “PieChart”. This was a composite component that contained a view stack. The view stack contained a progress bar and a Flex pie chart component. While data for the pie chart was loading, the progress bar portion of the view stack would be displayed, when the data was finished loading, the pie chart portion of the view stack would be displayed.
The number of instances of the PieChart was still increasing monotonically, even though it's parent ReportPod was now tamed. I pulled my hair out looking for event listener leaks in the PieChart, and eventually gave up. I reasoned that what's good for the goose is good for the gander, and created an object pool for charts. This was a relatively complex endeavor, and ultimately it failed. The leak remained.
The exercise in object pooling failed doubly. Although Flex 2 now supports reparenting, it's simply not reliable. The repartented components simply didn't work in the same way as freshly created objects. The most obvious symptom is that view stacks internal to the objects intermittently failed to respond to settings of their “selectedIndex”. A trace revealed that the selectedIndex was properly set, but on screen the view stack was not displaying the proper stack instance – but this happened only intermittently. There was not predictable pattern, but it was enough to make the pooled objects unusable.
It seemed I would have to figure out what was causing the leak, I just couldn't object pool my way around it.
Keeping it Simple
I went back to basics. I knew something in the PieChart was causing the leak – something about either its child components or its event listeners. So I created a very simple pie chart that did away with the view stack and displayed a very simple pie chart with static data. No memory leak.
I slowly added back functionality to the PieChart component. I added back the event listeners so that the component would be notified when data was available. I made the pie chart live so that it displayed real data coming from the back end server. Still, no memory leak.
Then I added back in the view stack with the progress bar. Bam! Memory leak. I replaced the ProgressBar component with a simple bit of text “Running Report...”. No memory leak.
It appeared that the ProgressBar component was the cause. I thought about it a bit, and it makes sense. As I can't really tell how long the report will run, I set indeterminant=”true” on the progress bar, which results in a progress bar that is constantly animating. A look at the source code for ProgressBar reveals that it uses a “Timer” object to accomplish this animation.
How do you use a Timer? You register an event listener for “TimerEvent.TIMER”, and it sends TimerEvents to your event handler periodically, based on the timer interval. Tracing through the ProgressBar source could I saw that this timer is never stopped, and the event listener for the timer is never removed. It seemed I'd found an event listener leak.
This is enough to cause the indeterminant ProgressBar to evade memory collection, as the internals of the Flex timer event dispatcher always retains a reference to the progress bar instance, in order to deliver timer events. But ProgressBar is a DisplayObject, with a “parent”, a reference to its parent container. And that parent container has a reference to its parent container, etc, etc... Suddenly an entire chain of components becomes reachable by the garbage collector. Thus the mere presence of a ProgressBar at some level in a visual component hierarchy can cause the entire component to evade garbage collection. Pretty nasty. Here's an example – don't leave it running to long, it basically makes my laptop unusable after a few minutes.
http://vanderblog.typepad.com/flex/ProgressLeak.html
This example creates a composite component containing a ProgressBar and an Image control. The Image control loads a large image from Wikipedia. A new instance of this component is created every second and added to the application. The previous instance is removed, and should be garbage collected, but is not, resulting in a rapid increase in memory utilization.
Summing up
I will grant that my usage of the ProgressBar was rather unique. Typically they are hosted in a relatively lightweight container, and displayed as a popup. In this situation the memory leaks caused will be small, and probably never noticed. I however included the ProgressBar in a view stack with some other very complex components with a large memory footprint, and proceeded to constantly create large numbers of new instances of these components. This was a recipe for disaster.
I've learned a lot about diagnosing memory leaks, and that their causes can be extremely obscure. You can be bitten even if you are doing everything by the book, and meticulously cleaning up your event listeners. There are no guarantees with the Flex framework – it does contain bugs, and it can contain memory leaks.
The new profiling tools in Flex Builder 3 were a significant help. The memory profiler makes it much easier to find the proximate cause of memory leaks, but as we saw in my situation, the root cause might not be the most obvious offender, it could be a deeply nested child. There is no magic bullet.
hello,
I am currently working on software for student attendance. I use the flex / flash and php to create the software but I faced a problem barcode scanner (Motorola-type barcode scanner Symbol LS2208 Laser Barcode Scanner – USB), can not connect to the application to call the data from the database when students scan the barcode printed on their student card.
Can you help me to make the script so that it can run on flex / flash and php. it makes me crazy for a week to seek a way out …. please help me (I have not mastered the java script)
thanks,
ps : i use flex / flash cs 5
table is: name, grade, address, barcode number, clock in, clock out. after all data is stored in the database, it’s time to call one by one using a barcode scanner. but only wrote the inputted barcode number on the barcode column that appears and do not call the data from the database in accordance with the code barcode from the student card.
Posted by: richard | December 20, 2010 at 02:41 AM