I recently had the pleasure of debugging an AS3 image viewer application that was crashing after an hour. It seemed to be some kind of memory leak, so I looked for objects not being cleared up by the Flash Player’s garbage collector. I found that, even with AS3’s mark and sweep garbage collection, BitmapData objects left stranded (with no external references) still hog RAM, even when their DisplayObject is deleted.
A similar problem existed in AS2, but will be more acute in AS3 I think, because removing a child DisplayObject from a display list does not delete it (as removeMovieClip() would in AS1/2). Try the following example with your Windows Task Manager or Mac Activity Monitor running, you should see what I mean:
import flash.display.* import flash.events.* // Add a main container sprite to the stage var main:Sprite = new Sprite(); addChild(main); // Add event listeners for mouse move and click stage.addEventListener(MouseEvent.MOUSE_MOVE, paint); stage.addEventListener(MouseEvent.CLICK, cleanup); // Draws bitmap noise at the mouse cursor function paint (e:MouseEvent):void { var b = new BitmapData(100,100,false,0xff0000); b.noise(int(Math.random()*int.MAX_VALUE)); var bmp = new Bitmap(b); var spr = new Sprite(); spr.x = e.stageX; spr.y = e.stageY; spr.addChild(bmp); main.addChild(spr); } // Removes all of main's children from the display list function cleanup (e:MouseEvent):void { while (main.numChildren>0) { main.removeChildAt(0); } }
Once the cleanup function runs, all of main’s sprites are removed from the display list, with no more references to them – so the garbage collector should clean things up. But it won’t dispose of the BitmapData objects automatically, so you eventually run out of RAM! This problem of memory hogging occurs even if you keep references to all the clips (in an array for example) and set them to null after removing them from the display hierarchy.
I can see a lot of problems occurring as developers move to AS3 without a firm understanding of how garbage collection works. One answer would be to manually dispose of BitmapData objects or handle the REMOVED_FROM_STAGE event and perform a cleanup as necessary. Here’s quick and dirty fix that cures the memory leak:
function cleanup (e:MouseEvent):void { while (main.numChildren>0) { main.getChildAt(0).getChildAt(0).bitmapData.dispose(); main.removeChildAt(0); } }
Tell me about it! I’m so supprised that Adobe haven’t included methods in the DisplayObject and its sub classes for clearing themselves up if you clearly want to delete them and their children completely. Of course, it’s necessary to write in specific clear up operations for some custom classes, but having built in methods for quickly destroying a Sprite (and any of its event listeners, bitmaps along with their bitmapdata, loaders etc) completely would be nice.
In fact, that’s inspired me to write a static class for my code bin with some cleanup scripts for DisplayObjects. Nice one!
First of all, I think too that Adobe should have implemented a complete destroy() or despose() method to DisplayObject. Anyway.. it seems that Sprite are referenced between each other (parent-child) by weak references. So when I delete a DisplayObjectContainer with many children and subchildren, they’ll all be stated for garbage collection (if they don’t have any other references). That’s at least what I believe after some tests with Flex Profiling. What strikes me though is that there’s no documentation about all this. And it’s really not obvious stuff at all. By the way, in my current project there are a lot of Bitmap instances who are all disposed without doing so explicitely (though those bitmaps are loaded, not created). Hmm… evil magic 😉
Oh man, your captcha wouldn’t load – a 15 min comment good bye!
Great! Thank you!
I always wanted to write in my blog something like that. Can I take part of your post to my site?
Of course, I will add backlink?
Regards, Reader