Thursday, May 28, 2009

Flex Memory Issue #3: Garbage Collection

Garbage collection is an in depth subject when it comes to modern programming languages. Wikipedia provides a general statement that summarizes what it is:

“…garbage collection (GC) is a form of automatic memory management. The garbage collector, or just collector, attempts to reclaim garbage, or memory used by objects that will never be accessed or mutated again by the application.“ http://en.wikipedia.org/wiki/Garbage_collection_(computer_science)

The expectation of a programmer in a language like Flex or Java, is that when widget X is no longer being used in the application that it gets removed from memory. That it is pretty simple scenario that is unrealistic, considering all graphical user interface components maintain references to other components which contain references to other components and so on and so forth. Flex also adds a few more layers of complexity by including modules and binding.

Flash Garbage Collection Basics

There is a must-read presentation on an atomic view of Flash Player garbage collection written by Alex Harui sometime in 2005, from which I obtained a lot of my high level information. Somehow I located the presentation through the internet and downloaded it, but I have since lost the link.

Flex garbage collection is synonymous with Flash garbage collection, since the memory being talked about is that of the Flash Player. Flash memory allocation basically works by grabbing chunks of memory from the Operating System, and carving those chunks when needed into smaller blocks of fixed size that together as a group are called a pool. When a pool is used up then another chunk is taken from the OS and becomes another pool.

A block of memory can be used, freed, or unused. Used means that there is something in there, freed means that something is still in there but it no longer used and can be garbage collected, and unused means that the block was freed and had been set to unused by the garbage collector.

clip_image002

This means that Flash garbage collection is based on allocation since more memory is not obtained from the OS until Flash needs some for itself but is out. Before Flash decides that another pool is needed though, it might attempt to run garbage collection. The garbage collector will move through Flash memory looking for freed blocks that can be set to unused.

There are also a few important things to note about garbage collection when looking at its effect on memory:

1. You generally can’t predict when garbage collection will run by itself

2. Garbage collection is based on allocation, so memory will remain constant in an idle application

3. Garbage collection is not guaranteed to mark all the blocks is can as unused in one pass, which means that memory may never returned to its initial point

For a more details about the Flash garbage collector Grant Skinners has several writings about it in his AS Resource Management series: http://www.gskinner.com/blog/archives/2006/06/as3_resource_ma.html

So why do I care about garbage collection?

It doesn’t always collect what you want it to when you want it to, and when dealing especially with modules it can lead to some behaviors that look like memory leaks. I have personally found that if you want to get memory under control with modules you absolutely have to force garbage collection. For example consider a test application in which I am loading and unloading the same three modules over an over again into the current application domain. I know, what is an application domain? For now don’t worry about because it is complicated and something that I intend to explain later in another article. All you need to be aware of that this is the default module behavior if you use the Module Manager to load a module without using any parameters.

image

Without getting into the details of application domains and their specific effects on memory behavior, you can at least see that if you don’t force garbage collection memory just keeps going up with little breaks in between. In the case of the application that I am describing it will eventually cause my browser to run out of memory and lock up once the Flash Player gets upwards of 120 MB. 

I have also found that the larger the modules and the more detailed the styles the more pronounced this problem. I believe that when I averaged out the increase in memory over time,  it ended up being about 1.5 MB per module load. If you force garbage collection after each module unload it ends up being pretty much a flat line, granted though other techniques were incorporated in this application to help memory along to be discussed in future articles. The important thing to note is the positive effect forced garbage collection has by itself.

Force garbage collection?

Junior Java programmers love to ask the question about how to force garbage collection, to which the reply is always that you can only suggest it. The same is not true for Flex, as there are unsupported techniques that have various effects in the debug and non-debug Flash Player as well as with AIR.

Method 1: System.gc()

According to the Adobe documentation (http://www.gskinner.com/blog/archives/2006/06/as3_resource_ma.html) this forces garbage collection, but only in AIR and the debug version of the Flash Player. In practice though this method doesn’t seem to be very effective for reasons listed below.

Method 2: System.gc() twice in a row

According to Sean Christmann’s post about kick starting the garbage collector (http://www.craftymind.com/2008/04/09/kick-starting-the-garbage-collector-in-actionscript-3-with-air/), if you call System.gc() twice in a row it is far more effective. Unfortunately though this only works in the debug Flash Player and AIR.

Method 3: The local connection hack

According to Grant Skinner’s third installment of this AS Resource Management Series (http://www.gskinner.com/blog/archives/2006/08/as3_resource_ma_2.html), you can force garbage collection by calling two local connections in a row:

try {
   new LocalConnection().connect('foo');
   new LocalConnection().connect('foo');
} catch (e:*) {}

In testing this I find that it works in AIR, the debug Flash Player, and the non-debug Flash Player. It is also the my preferred way of forcing garbage collection, even though it it not supported.

When do I force garbage collection?

I have engaged in several debates regarding when to force garbage collection and why, and the most important thing you need to understand is that forcing garbage collection will only clear objects that are eligible for collection in the first place. This means that if you write a loop in your application to force garbage collection at 70 MB until it goes down to 50 MB you are probably going to loop infinitely. Forcing garbage collection is not the silver bullet to all of your memory problems.

When to force garbage collection really depends on what your application is doing. For example if your application just consists of several screens you might try forcing garbage collection in between screens. In other applications it may be more appropriate to force it every few minutes.

How do I make objects eligible for garbage collection?

Now that is the million dollar question, and unfortunately there is no one answer. The purpose of this series on Flex memory issues is designed to help one thing at a time. Binding, states, modules, effects, application domains, complex components, listeners, and more will have their memory issues explained.

6 comments:

Unknown said...

Interesting, but i felt like i had to comment:

Forcing garbage collection may be risky in certain types of application. I've had cases where it just kept randomly breaking the remote server calls my application made, resulting in mysterious IOErrors (flex errors for http errors). I've tried for months to track what caused these errors until i found out the the forced garbage collect was the culprit.

If you are interested in another take on memory management, you could check Greg Skinner's blog articles :
http://www.gskinner.com/blog/archives/2006/06/as3_resource_ma.html

Unknown said...

Forced garbage collection is of course risky because System.gc() only works in AIR and the debug player, and the LocalConnection call is not supported.

While I am aware and even reference Mr. Skinner's writtings at times, forced garbage collection has become the only way most of us have ever been able to get memory under control in Flex and AIR applications in conjunction with other methods. You also cannot use the delete function on anything but local instances, which usually aren't the problems in large applications.

Flex memory is an interesting problem, and I have actually been able to get it under control in smaller application using other methods which I intend to eventually write about besides forcing garbage collection. Forcing garbage collecction with modules seems to be an absolute must though.

It is interesting that forcing garbage collection was causing remoting errors for you. Which method of forcing garbage collection were you using?

Unknown said...

The brief description of the internals of garbage collection is also very high level. There are other resources as you [quatrol] mentioned available for the details of how garbage collection works. My interest in this case is how to help memory along its way, particularly with modules.

Unknown said...

The method I used to force the garbage collection was the LocalConnection "hack" as the application is not AIR.

As for the 'delete' function/keyword, it only removes the target object's index from the associative array (as almost everything in flex is an associative array) thus also removing the reference and is only useful to remove dynamic properties from an object, or unused indexes from an array.

It shouldn't a have much bigger impact on memory than nulling the object's reference (but it allows you to get back the memory used by whatever the 'index' was (usually some string)).

ex:
toto.target = null;
This should let you eventually garbage collect whatever the target object was.

delete toto.target;
This should let you eventually garbage collect the target as well as the string used for its index in the object toto (ie. "target", and providing that this was a dynamic property).

And yeah, i noticed that your focus was modules, but felt like the possible remoting issue could be of interest to you and the other readers :)

Unknown said...

The issue with LocalConnection and causing remoting to fail greatly interests me as well. I will add a * to that method of garbage collection when I get home, in case someone uses it and doesn't read the comments.

Anonymous said...
This comment has been removed by a blog administrator.

Contributors