Flex optimization tip: ArrayCollection.removeAll() vs. ArrayCollection.source = new Array(). Is this a bug ?

One of the bugs reported on our recently released LogBook application was around the “Clear Logs” button. Within LogBook, we keep all the LogMessage messages in an ArrayCollection. When Clear Logs was clicked, we called a removeAll() function to clear the ArrayCollection. However when the size of the ArrayCollection grew more than a hundred or so, the clear command took a while and the whole application would freeze for a few seconds. A quick change of that to ArrayCollection.source = new Array() fixed the performance immediately. Interesting.
Looking deeper into the removeAll function and following the hierarchy to the ArrayList class, I saw that the removeAll loops through the entire source array, looking at each member to see if its an event dispatcher and if so calling a function to remove the propertyChange listener. Confused a bit, I posted a message on flexcoders to see why this was the case. Alex Harui from Adobe resonded with this:

Our code has to look like that otherwise if someone kept a reference to one of the objects that got removed, it would keep the ArrayList from being GC’d. We added listeners, so we should remove them. If the data objects are going away too, then you can probably reset the source, but if you see a memory leak later, I’d definitely check in that area.

Makes sense, but only kind of since the way the eventlistener was added was with the weak keys flag turned on, which means that I did reset the array, even if the old LogMessage objects were in memory, the garbage collector could still get rid of them whenever it ran. So I responded to Alex so:

Hmm, maybe I am missing something here? The only place I see addEventListener being called is

protected function startTrackUpdates(item:Object):void
{
if (item && (item is IEventDispatcher))
{
IEventDispatcher(item).addEventListener(
PropertyChangeEvent.PROPERTY_CHANGE,
itemUpdateHandler, false, 0, true);
}
}

which has the weak-keys boolean flag turned on. Shouldnt that allow garbage collection to run without explicitly calling removeEventListener ?

And Alex’s response:

Because of the way GC works, I still think it is best for us to remove the listeners. GC is only on allocation so again, if someone had a handle to an object that got removed and twiddled a value, the notification code would run needlessly until GC kicked in.

However, I think you’ve come up with a great optimization for folks who know they are destroying everything all at once.

So the final verdict: If you are destroying all the objects within the Arraycollection, use ArrayCollection.source = new Array() instead of ArrayCollection.removeAll(). Calling removeAll() on large ArrayCollections seems to be a really heavy operation and could kill responsiveness of the app.

I do feel that this is a bug and not a simple optimization. Here is the final email I sent to flexcoders a couple of minutes back:

Hi Alex,

Thanks for responding. Should this be tracked as a bug? I think there may be a better way of handling this:

On removeAll() you can set a flag like removeEventListenersPending = true, copy the old elements to a temporary new array and run through the it in smaller sized increments (removing event listeners from say 40 objects every enterframe using something like calllater). In the meanwhile if a property change event is fired, you can see if the flag is set and if so make sure the item is in the current source array and not the temp array. When all objects in the temp have had their event listeners removed, set the removeEventListenersPending = false.

I think the current implementation may be causing a lot of performace issues since a lot of people may be using ArrayCollections for large data sets (thats what Flex is great for :) ) and may not know of the penalty of the removeAll().

If anyone has any opinions, please feel free to comment. Okay, back to real work :).

15 Comments

  1. @maliboo,
    Nope, although I cant imagine that it will help. All that is supposed to do is disable CollectionChange Events broadcasts (instead putting them in an internal array to be broadcast on enableAutoUpdate). removeAll() triggers only one CollectionChange Event (RESET). So the problem doesnt seem to be a view receiving too many updates.

    Like

    Reply

  2. @maliboo,
    Nope, although I cant imagine that it will help. All that is supposed to do is disable CollectionChange Events broadcasts (instead putting them in an internal array to be broadcast on enableAutoUpdate). removeAll() triggers only one CollectionChange Event (RESET). So the problem doesnt seem to be a view receiving too many updates.

    Like

    Reply

  3. Hi I have an app increases the rows in arraycollection (manually), usually there are only a max of 10 rows added. When the process completes I use Removeall() as discussed. I may be doing this x 1000 times per session. It has been noted that the application appears to be slowing down after 600 / 700 processes. Does anyone think that the removeAll() function could have a cumalitive effect on the app?

    Like

    Reply

  4. @Paul,
    How many items are you removing at a time? The removeAll() function seems to need some process time that is dependent on the number of items being removed at one time (It basically runs in a for loop removing event listeners which becomes processor intensive for large ArrayCollection objects). If you are removing 10 at a time that should not be a big deal.
    Its more likely that you may be removing those objects from the ArrayCollection but these objects aren’t being garbage collected (maybe you have an event listener of your own that is not using weak references), so after a while, just having these objects in memory may cause a performance hit. Try using the Flex profiler to see if the objects are being garbage collected correctly.

    Like

    Reply

  5. Thanks for this article.
    For my project I extended the ArrayCollection class and overrode the removeall() function.

    Also, When I called removeAll on and arrayCollection it didn’t seem to dispatch an event, am I missing something? Anyway, in my class I also made it dispatch and event when removeall is called.

    Like

    Reply

  6. I am using arrayCollection.source = new array[]. But the old objects are not being garbaget collected. Can anyone please help me?

    var obj:Object = new Object;
    obj.name = “something”;
    arraycollection.addItem(obj);

    after some time,

    arraycollection.source = new Arrray();

    but ‘obj’, still exists in memory. Please help me

    Like

    Reply

  7. @RajPrabha
    1) obj will only get gc-ed if no other object has added an event listener to it. Make sure nothing else is forcing its existance.

    2)Objects running code like Timers, Enterframe listeners etc cannot be garbage collected until those stop. See http://www.gskinner.com/blog/archives/2006/07/as3_resource_ma_1.html

    3)Also note, objects marked for Garbage Collection may not be collected immediately. The Flash Player marks them for collection until the next GC cycle but that may take a few frames.

    Like

    Reply

  8. Hi it’s good article … as i understand it’s good way to do your program be faster, but not right way to use it … can some one give answer or reference where i can get answer to question, how use very large arrayCollection ? i had collection some about 60 000 of simple object .. for looping on them or remove all it’s take to much memory to me … if there a some way how i can do operation on that kind of arrayCollection or greates ( some about 1 000 000 object inside) to removeAll , sort, looping true those nodes …

    Like

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s