AS3: Resource Management pt 2

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:

  1. 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.

  2. 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:

  1. 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.

  2. 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.

Summary

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.

Grant Skinner

The "g" in gskinner. Also the "skinner".

@gskinner

47 Comments

  1. Damn. Good stuff to know. Guess we’re all gonna have to grow up. Even us old guys. 🙂

  2. Excellent article. Brings to light some non-obvious but incredibly important points.

  3. Great to know cheers. Although a bit scary. I blame Java. Can’t even clean up after itself – sheesh 🙂

  4. Great stuff. Sounds like system resource management is crucial moving forward. Thanks for staying on top of what’s important.

  5. Grant-great stuff. Makes me feel better that your researching these issues.

  6. Thanks everyone. These articles take a fair bit of work, so it’s great to hear that they’re useful to people. 🙂

  7. Agree with everyone’s comments. I’ve been porting over some of our code-based components from as2… A lot suprizingly translates very well, while there are a few gotchas. I do agree that keeping up on the display object references will certainly be a new burden. Thanks for bringing this to light.

  8. Excellent article–I learned a lot!

  9. I’m so happy, all my time spent worrying about memory usage and damning Delegate as evil have made me well prepared for all this.

    Here’s how you do it, one simple rule. If an instance of a class creates another instance of a class, it must also destroy this class. By destroy I mean tell the class to remove any variables that might keep it alive. Unloading should occur exactly opposite to loading.

    That’s how I do it anyway. It makes it easier to keep everything under control on larger projects.

  10. Great article Grant !

    dying for the strategies now 😉

  11. excellent article. thanks!

  12. Interesting article Grant; especially the hint at “Issue #4” is a matter of non-programming background people who have yet to deal with garbage collecting. I’ve known of GC in the past but thought I was just lucky not to deal with the level of effort you are talking about.

  13. Another great article :).

    But, have you test with the weak reference flag set to true in addEventListener ?

  14. Cédric,

    You’re still jumping the gun. Weak event references will be in the next article. 😉

    They wouldn’t help with any of the issues above though, except for content never getting picked up by the GC because of a strong event reference.

  15. Tore Jørgensen July 13, 2006 at 3:41am

    I really hope the timeline problem will be at least improved before release. A quick test showed that while using actionscript to add or remove movieclips trigger flash.events.Event.ADDED and flash.events.Event.REMOVED, movieclips added and removed with keyframes in the timeline does not trigger those events. So it seems it isn’t even possible to use a listener to clean up?

  16. Great article. Looks like there are some big potholes in the road. Nice to have some advance warning. Best practices, although important now, will become a must.

    BTW. I know FP9 is alpha but, on the loading example I get this error in a dialog box:

    Error #2044: Unhandled IOErrorEvent:. text=Error #2036: Load Never Completed.

  17. Keep up the great education you are giving us flash people who have been pampered till flash 8 and suddenly shown the real world by MM/Adobe 🙂

  18. Hi,

    in yor first article, you were talking about two mechanisms of GC. The first one was that simple counting of references. Has this been disestablished in Flash Player 9? I ask myself, why Flash Player 9 doesn’t have both mechanisms, so people could avoid having problems, if they really would kill all references.

  19. Hi All,

    I have been reading through all of this resource managment information and it’s wonderful thing to be kept up to par. I have some questions and don’t want to clutter this blog. If anyone, Grant feels like they could chime in could they discuss some of my views and help me to clear up some of my perceptions. My blog relating to this topic is at the following URL: everything4me.com/ComSvr/blogs/sample_weblog/archive/2006/08/02/24.aspx

  20. Great articles Grant, much learned, thanks so much!

  21. mohammed barqawi September 14, 2006 at 5:05am

    hi thank about the article

    i finished developing a KIOASK application using flex and i found that if i run the application for one hour it eat all 50 mega from the ram , i am stuck , how should fix that

  22. Nice article. Tore Jørgensen’s post brings up a point that I am trying to solve, unsuccessfully. For whatever reason, MovieClips on a keyframe do not dispatch Event.REMOVED events. If you have a MovieClip on frame 2 of the main timeline, and a blank keyframe on frame 3, only 1 Event.ADDED event gets dispatched on frame 2, with the event.target being a “Shape” object inside that MovieClip, and no event.REMOVED events at all. Very troubling…

  23. Hi,

    I have this strange GC problem with the loader class;

    When I load an image and unload it later on. It won’t be disposed by the garbage collector.

    There are no references to neither the Loader nor the (weak) eventlisteners. After loading the image a couple of times the memory goes sky high 🙁

    Here is a (simple) example:

    //this example loads an image, unloads it as soon as it’s loaded, and loads another one….

    package {

    import flash.display.Sprite;

    import flash.display.Loader;

    import flash.events.Event;

    import flash.net.URLRequest;

    import flash.system.System;

    public class Application extends Sprite{

    private var currentLdr:Loader;

    private var request:URLRequest;

    function Application(){

    this.request = new URLRequest(“http://www.flashfocus.nl/forum/image.php?u=8105&dateline=1182191022”);

    this.loadNextImage();

    }

    private function loadNextImage(){

    this.currentLdr = new Loader();

    this.currentLdr.contentLoaderInfo.addEventListener(Event.COMPLETE, this.loadCompleteHandler,false,0,true);

    this.currentLdr.contentLoaderInfo.addEventListener(Event.UNLOAD, this.unloadCompleteHandler,false,0,true);

    this.currentLdr.load(this.request);

    trace(“Current mem: ” + System.totalMemory);

    }

    private function loadCompleteHandler(event:Event):void{

    this.currentLdr.unload();

    }

    private function unloadCompleteHandler(event:Event):void{

    this.loadNextImage();

    }

    }

    }

    //

    Do you have an explaination on why de GC doesn’t clean the loaded content?

    Hope you can help me out… tnx!

    PS: as you already mentioned in you post, using the unload method on the loader does not affect the memory. 🙁

  24. I found the solution on in this post:

    http://www.webforumz.com/macromedia-flash/42690-memory-leak-when-loading-swf-in.htm

    It seems that the unload() method has a bug when used in the flash IDE. Once I ran the test in a browser there seems to be no problem and the GC nicely clears the memory…

    Hope this will help people who had the same problem.

  25. Simply great article… cleared lot of points about AS3. Looks like you have lot of RnD. Thanks grant

  26. Thank you for the useful informations.

  27. Hi! Very helpful article!

    But… don’t you think it would be a big help if they offer a method to entirely make dissapear the DO, its childs and event listeners?

    It would be much easier and GC skills would still be useful.

    Greetings from Argentina!

  28. This appears to be an even bigger problem with AIR applications. Calling the double localConnections doesn’t seem to cause the GC, and it seems like the GC doesn’t run at all on its own either. I’ve got all my listeners set as weak, but the memory just keeps going up, never down. I start at 32K and can jump to 120K in just a few minutes.

  29. GC is with as3 is complete crap. Sophisticated languages that need sophisticated memory management, need sophisticated objects to manage memory! AS3 nothing in the way of memory management! I’m loading external swf’s (which contain 300+ PNG sequence, also where Flash absoloutly FAILS with) which ramps the ram usage to 300 megs per swf (GOD I HATE FLASH). So aside from growing past 2 gigs of usage, I decided I should unload the swf’s everytime they end (ready to start again) in hopes of this application actually running. However- with ZERO references to the dynamically loaded (with Loader) swf’s, unload() does NOT trigger any GC of anykind- ARRRRG

  30. Great article, thank you. I am running into these problems myself… I have a series of linked animations playing, each with attached addFrameScripts on their last frame… and sometimes the removed clip’s framescripts are triggering during the newer clip’s animation…from the grave as it were. setting the framescripts to null as they are removed does not help. very frustrating. looking for solutions!

  31. Fantastic articles, especially for those of us who are trying to bridge the Designer / Developer gap. Much praise for your efforts.

  32. Issue 2: Loaded Content

    Error #2044: Unhandled IOErrorEvent:. text=Error #2036: Load Never Completed.

    package {

    import flash.display.*;

    import flash.events.*;

    import Demo;

    public class Clip extends Sprite {

    private var count:uint = 0;

    public function Clip() {

    addEventListener(Event.ENTER_FRAME,handleEnterFrame);

    addEventListener(Event.ADDED_TO_STAGE,apply);

    }

    public function apply(p_evt:Event){

    Demo.out(“Clip instantiated~~~~~~~”);

    removeEventListener(Event.ADDED_TO_STAGE,apply);

    }

    private function handleEnterFrame(p_evt:Event):void {

    count++;

    Demo.out(“running: “+count);

    }

    }

    }

  33. Grant,

    Really enlightening articles – thanks!

  34. Thank you for your insite, I am dealing with this very issue.

    Look forward to your next post

  35. WTF – yes!

    WTF!!!

    Who gives a F about java developers? just because something stucks ass, doesn’t mean this should as well!

    Adobe – set your asses in motion!

  36. Great article, Thx a lot, i finally understand my problems with a shell loading external as2 & as3 swf after few load & unload.

    I just can’t believe what i’m reading ! Why Adobe don’t implement cleaning method for external loaded content ?

    I can’t rebuilt all the old externals swf (many dev, many people )

    I have just to stop my devs in as3, rebuilt them in as2 and forget about as3 for a moment. Hard work direct on my personal garbage 🙁

    Very sad about this, i was confident to have a work around to implement a cleaning method in my shell without deal with external content programming.

    I hope that Adobe will understand than learning new language is a lot of work, but recode all past dev is just impossible in many cases.

    Whatever Thanks again for your blog and my apologize for my english :), have a nice day.

  37. Puh, I m searching now for decades for concret

    code snipps/ examples to resolve the memory problems ;-). I learned a lot about defining, removing eventlisteners, weakly referencing etc.

    BUT:

    Please can someone give a code snipp

    how to defenetly remove objects from memory?

    And can someone please translate/illustrate this statement with an example:

    Make sure you clean up Asynchronous objects directly, nulling these

    objects doesn’t unhook them from the Flash player.

    – Timer

    – Loader

    – URLLoader

    – File/SQLite

    What means “clean up Asynchronous objects directly”???

    Should we write something like this:

    closing=”cleanMem()”

    private function cleanMem():void{

    …[WHAT NOW ???]…

    }

    Should we use deleting properties to remove an object from memory? I am very confused and disapointed about this issues.

  38. Sorry for unrelated post, the user “reynhart” who posted a comment a while ago, that is crazy I thought I was the only reynhart family in the world! Where abouts are you from?

  39. Hi Grant,

    I take your first demo code (Issue 1: Dynamic Content) and I added direct call to Garbage Collector (flash.system.System.gc();, twice) in order to test if event listeners, added WITHOUT weak reference ( addEventListener(Event.ENTER_FRAME,handleEnterFrame);) , keep the object in memory despite the fact that garbage collector ran.

    This doesn’t happen!!

    As soon as garbage collector has been called, handleEnterFrame stops run.

    It seems Garbage Collector doesn’t care about listener reference and still remove object from memory (in another flash project I could prove it monitoring memory through System.totalMemory).

    Please, could you help solve this issue?

  40. you try hardly, search whole google to solve this issue. Or alternatively, you wait for the next flashplayer version? On my understanding about flashplayer, it is not a choice in case the resources management must be prior.

    And also, it is always better if you handle yet the last task in your list 😀 😀

    btw, thank you for sharing this exciting article. i love flash, i love it ^_^

  41. Hi all. In my project, I am doing the following:

    I have few movieclip instances that I add at design time on 3 different frames with the same name. Lets say with the name “abc”.

    I also load some external images in dynamically created sprites or movieClips.

    Sometimes I need to bring the movieclip instance “abc” at the highest childindex (ie. in front of the dynamically loaded content too.

    After bringing it to front, if I goto to other frame and trace all the obect names i see that there are two instances with the name “abc”. How does this happen? Its causing issues because i am not able to hide the “abc” movieclip once the frame changes since there are then, two instances named “abc”. Can u help me with this?

  42. So guys asked about error #2036 wich means that the loading never stops. That error cant be handled by try/catch.

    Does someone know how is possible to solve the issue???

  43. So I have found the solution for the error #2036

    Guys are discussing it on

    http://www.mail-archive.com/flexcoders@yahoogroups.com/msg54478.html

    they have a code there, it has a mistake, the right code is

    function myPictureLoader() {

    var pictLdr:Loader = new Loader();

    var pictURL:String = “photos/NzPicture.jpg”

    var pictURLReq:URLRequest = new URLRequest(pictURL);

    pictLdr.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, handleIOError);

    pictLdr.load(pictURLReq); }

    function handleIOError(event:IOErrorEvent) : void {

    Alert.show(‘file not found’, ‘IO error’);

    }

  44. TheDarkIn1978 July 23, 2010 at 12:46am

    I thought i knew everything there was to know about GC in AS3 after reading this information a long time ago, but i just read page 694 of Colin Moock’s book about the BitmapData class’s instance method dispose() for immediately freeing bitmap data without waiting for the GC to mark and sweep. perhaps you haven’t mentioned it because bitmaps can be released without calling dispose() first, unlike forgetting to removing an event listener.

  45. Great article, even now. Still much relevance to be had.

Comments are closed.