ActionScript 3 has empowered Flash developers with faster code execution and a ton of API enhancements. Unfortunately, it has also led to the need for a much higher level of developer responsibility than ever before. In order to prepare and educate developers on how to deal with some of this new responsibility, I am writing a series of articles on resource management in AS3, Flex 2, and Flash 9. The first of these articles discussed the mechanics of the Garbage Collector in Flash Player 9. This article will focus on the implications some of the new features of AS3 have on resource management, and the potential headaches they could cause you even in simple projects. The next article in the series will introduce some of the new tools we have at our disposal to deal with these issues.
The biggest change in AS3 that affects resource management is the new display list model. In Flash Player 8 and below, when a display object was removed from the screen (with removeMovie or unloadMovie), it and all of its descendants were immediately removed from memory, and halted all code execution. Flash Player 9 introduces a much more flexible display list model, where display objects (Sprites, MovieClips, etc) are treated the same as normal objects. This means that developers can now do really cool things like reparenting (moving a DO from one display list to another), and instantiating display objects from loaded SWFs. Unfortunately, it also means that display objects are now treated the same as every other object by the Garbage Collector, which raises a whole slew of interesting (and possibly non-obvious) issues.
Issue 1: Dynamic Content
One of the more obvious issues is related to Sprites (or other DOs) that you instantiate dynamically, then wish to remove at a later time. Because display object’s no longer live and die on the display list, when you remove the object from the stage it continues to exist in memory. If you have not cleaned up all other references to the clip, including the object’s listeners, it may never be removed. If you have done a good job of cleaning up all references, the clip will be removed from memory the next time the GC runs a sweep, which is at some indeterminate point in the future based loosely on memory usage (see my previous article on Garbage Collection in AS3 for more information).
It is very important to note that not only will the display object continue to use memory, it will also continue to execute any “idle” code, such as Timers, enterFrames, and listeners outside its scope. A couple of examples may help illustrate this issue:
- You have a game sprite that subscribes to its own enterFrame event. Every frame it moves and carries out some calculations to determine it’s proximity to other game elements. In AS3, even after you remove it from the display list and null all references to it, it will continue to run that code every frame until it is removed by garbage collection. You must remember to explicitly remove the enterFrame listener when the sprite is removed.
- Consider a MovieClip that follows the mouse by subscribing to the stage’s mouseMove event (which is the only way to achieve this effect in the new event model). Unless you remember to remove the listener, the clip will continue to execute code every time the mouse is moved, even after the clip is “deleted”. By default, the clip will execute forever, as a reference to it exists from the stage for event dispatch (we will look at how to avoid this in the next article).
Now imagine the implications of instantiating and removing a bunch of sprites before the GC does a sweep, or if you failed to remove all references. You could inadvertently max out the CPU fairly easily, slowing your application or game to a crawl, or even stalling the users’ computers entirely. There is NO WAY to force the Flash Player to kill a display object and stop it executing. You must do this manually when it is removed from the display. I will examine strategies to manage this task in a future article.
Here’s a simple example (Flash Player 9 required). Click the “create” button to create a new Sprite instance. The sprite instance will start outputting a counter. Click remove and note how the output continues, despite the fact that all references to the sprite have been nulled. You can create multiple instances to see how this issue compounds over the life of an application. Source code is available at the end of this article.
Issue 2: Loaded Content
If you consider that the contents of loaded SWFs are also now treated the same as every other object, it is easy to imagine some of the problems that you can encounter with loaded content. Just as with other display objects, there is no way to explicitly remove a loaded SWF and its contents from memory, or to stop it from executing. Calling Loader.unload simply nulls the loader’s reference to the SWF, it still has to be picked up by a GC sweep (assuming all other references to it have been properly cleared).
Consider the following two scenarios:
- You build a shell that loads your experimental Flash pieces. This experimental work is cutting edge, and pushes the CPU to the limits. A user clicks a button to load one experiment, views it, then clicks a button to load a second experiment. Even if all references are cleared to the first experiment, it will continue to run in the background, which will likely max the processor out when the second experiment starts running at the same time.
- A client commissions you to build an application that loads AS3 SWFs created by other developers. These developers add listeners to the stage, or otherwise create external references to their own content. You now have no way of unloading their content, it will live in memory, and consume CPU until the user quits your application. Even if their content does not have any external references, it will continue to execute indefinitely until the next garbage collection sweep.
For security sensitive projects, it is very important to understand that if you load third party content, you have no way of controlling when it is unloaded, or what it executes. It would be VERY easy to write a SWF that once loaded into your application continued to execute in the background and captured / transmitted user input / interactions, even after being unloaded.
Another simple example. Exactly the same scenario as in Issue 1, but loading a SWF each time instead of using dynamic instantiation.
Issue 3: The Timeline
This one is mostly an issue for those playing with Flash 9 public alpha, and it is important to remember that it is alpha. Hopefully this gets resolved before beta or final.
The timeline in AS3 is code driven. It dynamically instantiates and removes display objects as the playhead moves. This means it is subject to the same problems as those listed in issue 1. Clips that are removed from the screen due to timeline updates will remain in memory, and continue to execute any idle code on them until they are picked up by the GC. Not really expected behaviour for Flash developers, and certainly not for Flash designers (who shouldn’t have to think about GC at all).
Here’s another quick example. Same concept, but this time it is jumping between two frames to instantiate / remove a clip.
What is Adobe thinking? Or alternatively, why is this an issue?
Flash Developers are likely looking at this, and thinking WTF, this is a nightmare!? On the other hand, Java developers are probably looking at it and saying “so what?”. This disparity is understandable – Flash developers are not used to having to do manual resource management beyond basic best practices (ex. kill references when you’re done), whereas Java developers have been through all this before. These issues are par for the course for most modern memory managed languages, and unfortunately there is no way to completely avoid them.
On the other hand, Flash raises a lot of challenges that are rare in other languages (including Flex for the most part). Flash content tends to have a lot of idle / reactive code execution, whereas Java and Flex are mostly interactive (ie. CPU intensive code usually only executes based on a user interaction). Flash projects load external content from third party sources (possibly with poor coding standards) far more often as well. Flash developers also have fewer tools, profilers and frameworks to utilize. Finally, Flash developers generally come from a much less formal programming background – most Flash developers I know have backgrounds in Music, Art, Business, Philosophy or just about anything but programming. This diversity results in AWESOME creativity and content, but does not really prepare the community for dealing with resource management issues.
Resource management is going to be an important part of AS3 development. Ignoring the issue could result in sluggish content, and potentially stalling users’ systems completely. There is no longer any way to explicitly remove a display object from memory and stop its code from executing, which means we have a responsibility to clean up properly after our objects. Over the next few weeks I will outline some tools and strategies for tackling these issues. Hopefully as a community we can establish best practices and frameworks to make this transition easier.
You can download the source code for the demos by clicking here.
I will continue to update these articles with the latest information and community input as it becomes available, so you may want to check back occasionally.