Following my post yesterday about the addition of Loader.unloadAndStop to the Flash Player 10 API, I chatted with Werner Sharp, who is one of the Adobe engineers that worked on the feature (and an all round good guy). Following that discussion, I thought it would be helpful to post a summary of what I know about it, and disseminate some of the information Werner provided.
The unloadAndStop method was added in response to challenges ActionScript developers were facing with halting loaded SWF content, and removing it from memory. These issues were detailed in an article I posted in early April titled "Failure to Unload: Flash Player 9's Dirty Secret".
This API addition is not a magic bullet for solving all of the listed problems. What it does do, is attempt to prepare the loaded content for collection, and then hint to the Garbage Collector that it should initiate a collection pass. You can prevent the latter by passing false as the first parameter. It is still easy to get a SWF stuck in memory, but it is much harder to do accidentally.
Here is the list of things that unloadAndStop does to prepare loaded SWFs for collection:
- Stops all MovieClips
- Stops all sounds playing/streaming
- Stop/removes all Timer objects
- Remove all global listeners for enterFrame, exitFrame, frameconstructed, activate, deactivate
- Remove all stage listeners that have been created by the child.
- Closes all NetConnection/NetStream
- Video.attachNetStream/attachCamera(0)
- Microphone.setLoopback(0)
- Removes AS3 fonts from the global font table
- Stops sockets, xmlsockets, filereference downloads, other downloading objects (grandchildren SWF), etc.
- Frees bitmap related to cacheAsBitmap/filter, etc.
Note that all of the above are run recursively, so they will also apply to any nested children, including other loaded SWFs.
There have been a few early questions about unloadAndStop that I will try to answer:
Why wasn't this functionality just built into the unload method?
There are a couple good reasons for this. The first is backwards compatibility. This is a pretty major change in real functionality, if not expected functionality, and changing it could break a lot of content. This is also a very invasive function, and could have weird effects in some circumstances. For instance, if you loaded a SWF, made a reference to a child of that SWF, called unloadAndStop, then reparented that child into your main application things could get weird because that child DisplayObject has already been "stopped".
What types of things will keep the SWF in memory?
This API does not null external references to your SWF, or to elements in the SWF - doing so would be very processor intensive in large projects. As such, any hard references to the SWF or objects within it will prevent collection. For example, if your main application were to reference a movie clip in the loaded SWF such as:
var myFunClip:MovieClip = myLoader.content.funClip;
You can also do this inadvertently with events. unloadAndStop will remove listeners that the loaded SWF created on the stage (for listening to MOUSE_MOVE events, for example), but not in other locations. Something as simple as the following code in your SWFs main timeline can prevent it from unloading, because it will create a hard reference back to it:
parent.parent.addEventListener(Event.CHANGE,myFunction);
As you can see, unloadAndStop isn't perfect, but in conjunction with fixes for the bugs I mentioned in my article, it's a solid step in the right direction. That doesn't mean I'm going to stop pushing for content sandboxing and explicit unloading though. :)
Comments (12)
I know you used it as a simple example, but we would never use 'parent.parent.addEventListener(Event.CHANGE,myFunction);', wouldn't we? Very bad OOP practice that asks for problems.
Posted by: loeribas at July 5, 2008 02:24 AMBut it's ok you make us aware of these kind of memory householding things.
URL: http://arievanboxel.nl
Really good news yes, we are heading in the right direction :)
Posted by: Thibault Imbert at July 5, 2008 05:13 AMURL: http://www.bytearray.org
loeribas,
Yes, the example would definitely be an example of poor OOP architecture. Of course, that doesn't stop some people from doing it. It's also an example of bad practice because it doesn't use weak references for the listener - the following simple change to that line of code would ensure it did not prevent collection:
Posted by: Grant Skinner at July 5, 2008 10:45 AMparent.parent.addEventListener(Event.CHANGE,myFunction,false,0,true);
More info:
http://www.gskinner.com/blog/archives/2006/07/as3_weakly_refe.html
URL: http://gskinner.com/blog/
Hi Grant,
Great info as always.
Just wanted to ask, your Janitor class, does it do the same thing as the new unloadAndStop?
Thanks
Posted by: H Archer at July 5, 2008 06:21 PMURL:
Hi Grant,
first of all thank you so much on enlightening us on the neccessary topic of GarbageCollection.
Now, just like H.Archer above, I do have questions about your Janitor: I do not find any documentation on how to implement it into my sourcecode other than page 32 of your flash in http://gskinner.com/talks/resource-management/ :
++++++++++++++++++++++++++++++++++++++++++++++
import com.gskinner.utils.Janitor ;
var scruffy = new Janitor(this);
scruffy.addSoundChannel(sndChannel);
scruffy.addEventLstener(obj,"type",handler);
scruffy.addConnection(myLoader);
scruffy.addTimer(myTimer);
scruffy.addInterval(timerID);
scruffy.addDisposable(myBmpData);
scruffy.cleanUp();
+++++++++++++++++++++++++++++++++++++++++++++
1.) Now my first problem is that I instantly get the errors that scruffy would be undefined.
2.) Next I do not exactly understand this example in terms of declaration: Do I have to declare my arrays, objects, listeners etc first and then let them run through the scruffy Janitor, or is that addInterval... already a declaration ?
3. Does the Janitor Class replace all necesities for me to use weak references and your other utilities ?
I really would apprecciate a tiny, but complete example containing all your classes like the StageStatusNotifier, I definately want to implement your code into my FlexProject.
Thank you very much !
Posted by: Thilo at July 6, 2008 12:03 PMURL: http://CyMeP.com
Grant, thanks for posting this and your previous posts about this general problem.
The other way a SWF can be locked in memory is if the parent SWF reaches into the child and attaches listeners. Flex's FocusManager can do this and does not uses weak references.
For anyone reading, please test this API if possible with the latest Flash player build and report any findings. We've tried hard to get the issue resolved for well formed applications that do common things like have timers or stage listeners or enterframe listeners. Any feedback on the new API is appreciated.
Werner Sharp
Posted by: Werner Sharp at July 7, 2008 02:18 PMFlash Player Engineering
URL: http://www.adobe.com
Hey Grant. Long time reader, first time commenter.
One big problem I have with external content is if you load an external swf which has a doc class with some getters that return a new instance of a display object that is contained in that (external) swf's library. (the main reason for this method is ease for artists to created content.)
The second you add this swf to the parent swf's display list and keep it there for longer than 1-2 frames, remove it from the display list, that swf will NEVER get garbage collected in Flash 9. No listeners have been applied, the loader was nulled, load events removed immediately... so I am a loss on that issue. ( my 'solution' is to keep each loaded swf in ram so I can call new instances of the library content I am looking for. It sucks but it keeps from having 60 some odd copies of 1 swf in ram)
Hopefully the above was clear enough to understand ^_^
Posted by: Louis Tovar at July 7, 2008 04:58 PMURL: http://deadlock32.newgrounds.com
I was a little confused by Werner's post, so I contacted him for clarification. Listening to an event on a loaded child will not prevent it from properly unloading.
He was trying to point out that parent content can also create situations where the child is not unloaded. For example, if a parent reaches into a child and adds a listener back to itself. Not a good practice of course, but worth being aware of.
// in the parent:
Posted by: Grant Skinner at July 7, 2008 05:09 PMthis.addEventListener(Event.CHANGE, myLoader.content.childFunction);
URL: http://gskinner.com/blog/
For the most part, it looks like this should reduce weight on the proc until the gc makes a pass. I wonder though, does it have any scope limitations or Objects that the method does not access? I'm thinking Arrays and anonymous Objects... Does the cost of running these operations at any point become greater than the cost of hinting the gc to make a pass? Does this hinting at the gc have the same effect as System.gc()?
I have just one more question: in the case of an object in the loaded swf listening to events of an object in the parent domain, the object in the loaded swf is referenced because the object being listened to in the parent domain has added the object in the loaded swf to its listener list - is this correct?
Thank you.
Posted by: Melih Elibol at July 17, 2008 12:51 AMURL: http://blog.computerelibol.com
Louis Tovar,
You said your SWF had getter methods for returning an instance of a DisplayObject, perhaps you forgot to remove that instance from the display list? The SWF won't get GC'd until all objects you retrieved from it's library have been removed from memory.
Posted by: Andres A at August 6, 2008 01:33 AMURL:
I'm trying out unloadAndStop methods on a little bigger project, but loaded swfs are still not unloaded. I know, unloadAndStop is just "attempting" to prepare loaded SWF for unloading. But I guess what we really need is some method that "force" loaded SWF to unload no matter what.
Do you know any good way how to observe and find where in application we have memory leaks?
Do you have any info if in PARENT movie which is compiled for FP10 you are using unloadAndStop but loaded SWFs are compiled for FP9? Does it still works?
Thx
Grega
P.S.: Your janitor class is great !!!
Posted by: Gregor Cepek at August 15, 2008 04:51 PMURL:
@loeribas: Sometimes you want to listen for events on the main application object. e.g. the "resize" event. In that case you'll end up with a reference from the main application to the loaded application, unless you use a weak reference (which you should).
@Werner Sharp: Flex's FocusManager does hold references to objects inside the loaded SWF, but it releases the references when the objects are taken off stage; so that shouldn't be a concern.
Posted by: Manish Jethani at August 18, 2008 05:21 PMURL: http://manishjethani.com/