Posted on July 5, 2006 by Grant SkinnerActionScript 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.
Articles in this series
Related articles
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:
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:
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.
Follow @gskinner on Twitter for more news and views on interactive media.
|
|
|
47 Comments
Damn. Good stuff to know. Guess we're all gonna have to grow up. Even us old guys. :)
Posted by: Keith Peters on Jul 5, 2006 11:10am URL: http://www.bit-101.com
Excellent article. Brings to light some non-obvious but incredibly important points.
Posted by: Ash on Jul 5, 2006 11:34am
Great to know cheers. Although a bit scary. I blame Java. Can't even clean up after itself - sheesh :-)
Posted by: Mike on Jul 5, 2006 1:04pm
Great stuff. Sounds like system resource management is crucial moving forward. Thanks for staying on top of what's important.
Posted by: Michael DelGaudio on Jul 5, 2006 1:55pm URL: http://michaeldelgaudio.com
Grant-great stuff. Makes me feel better that your researching these issues.
Posted by: ethan on Jul 5, 2006 2:33pm
Thanks everyone. These articles take a fair bit of work, so it's great to hear that they're useful to people. :)
Posted by: Grant Skinner on Jul 5, 2006 4:31pm URL: http://gskinner.com/
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.
Posted by: Ian T on Jul 5, 2006 6:19pm
Excellent article--I learned a lot!
Posted by: Robert Penner on Jul 5, 2006 11:37pm URL: http://robertpenner.com
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.
Posted by: Max on Jul 6, 2006 3:22am
Great article Grant !
dying for the strategies now ;)
Posted by: Thibault Imbert on Jul 6, 2006 4:57am URL: http://www.envrac.org
excellent article. thanks!
Posted by: good@very.com on Jul 6, 2006 11:23am URL: http://none
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.
Posted by: Mark Lapasa on Jul 6, 2006 11:54am URL: http://knowledge.lapasa.net
Another great article :).
But, have you test with the weak reference flag set to true in addEventListener ?
Posted by: Cédric Néhémie on Jul 7, 2006 2:28am URL: http://book.abe.free.fr/blog/
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.
Posted by: Grant Skinner on Jul 7, 2006 9:07am URL: http://gskinner.com/
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?
Posted by: Tore Jørgensen on Jul 13, 2006 3:41am
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.
Posted by: Andrew E on Jul 13, 2006 3:38pm
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 :)
Posted by: Supriya on Jul 19, 2006 5:32am
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.
Posted by: Sven on Jul 23, 2006 3:16am
nice
Posted by: jack on Jul 25, 2006 8:21am
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
Posted by: Metallikiller on Aug 2, 2006 12:03am URL: http://www.everything4me.com/comsvr/
Great articles Grant, much learned, thanks so much!
Posted by: nwebb on Sep 1, 2006 9:03am
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
Posted by: mohammed barqawi on Sep 14, 2006 5:05am
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...
Posted by: Jesse on Nov 8, 2006 12:17pm
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. :(
Posted by: Arno van Oordt on Jun 18, 2007 1:23pm URL: http://blog.justgreat.nl
PPS: here you can find the complete example:
http://blog.justgreat.nl/wp-content/uploads/2007/06/loadertest.zip
Posted by: Arno van Oordt on Jun 18, 2007 1:43pm URL: http://blog.justgreat.nl
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.
Posted by: Arno van Oordt on Jun 19, 2007 3:15pm URL: http://blog.justgreat.nl
Simply great article... cleared lot of points about AS3. Looks like you have lot of RnD. Thanks grant
Posted by: inder on Aug 14, 2007 2:11am
Thank you for the useful informations.
Posted by: Bank zdjec on Aug 16, 2007 4:40pm URL: http://www.buy-photo.eu
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!
Posted by: Ces on Aug 16, 2007 8:23pm
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.
Posted by: Jason on Aug 17, 2007 12:32am
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
Posted by: DivineAnarchy on Dec 21, 2007 9:58pm
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!
Posted by: Sake Boy on Mar 11, 2008 11:03am
Fantastic articles, especially for those of us who are trying to bridge the Designer / Developer gap. Much praise for your efforts.
Posted by: mediapathic on Aug 29, 2008 3:52am URL: http://mediapathic.net
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);
}
}
}
Posted by: jogger on Sep 16, 2008 9:03pm
Grant,
Really enlightening articles - thanks!
Posted by: Petit on Oct 10, 2008 10:40am URL: http://petitpub.com
Thank you for your insite, I am dealing with this very issue.
Look forward to your next post
Posted by: Alan Sweeney on Oct 14, 2008 1:55pm
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!
Posted by: grabek on Nov 27, 2008 6:28am URL: http://www.compelo.com/
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.
Posted by: reynhart on Jan 24, 2009 8:42am
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.
Posted by: stevie on Mar 1, 2009 5:55am
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?
Posted by: Ben Reynhart on Mar 3, 2009 4:55pm URL: http://www.reynhart.com
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?
Posted by: Saul on May 26, 2009 5:20am
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 :D :D
btw, thank you for sharing this exciting article. i love flash, i love it ^_^
Posted by: who on Jun 2, 2009 10:18pm
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?
Posted by: Hardik on Sep 11, 2009 2:46am
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???
Posted by: AlexG on Dec 16, 2009 7:22pm URL: http://alexgblog.com
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');
}
Posted by: AlexG on Dec 16, 2009 7:34pm URL: http://alexgblog.com
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.
Posted by: TheDarkIn1978 on Jul 23, 2010 12:46am
Great article, even now. Still much relevance to be had.
Posted by: Kawika on Jun 3, 2011 3:00pm URL: http://www.heftelstudios.com