Tuesday, April 13, 2010

AS3 memory management

...is a pain in the butt.

I've known for years that the AS3 GC (Garbage Collector) runs through and cleans junk up when it is ready to, but this is somewhat crazy.

Let's say you have a Sprite all set up called "Ship", and you blow it up on the screen and then want to remove the ship.
// remove the child from the display list...I'm using GameElements here
// could just as easily be the root

GameElements.removeChild(ship);


// and set the actual ship var to null

ship = null;
Now, the ship should be deleted and done, right? Wouldn't that be super? Turns out that you can no longer reference the ship, but Flash still has it sitting around in memory until it decides the time is right to remove it. This could take seconds or hours (or it may never remove it).

The problem gets worse if you use the Flash frames, apparently. (I'm kind of guessing on this one). If you use Flash frames, everything you have populated onto the frame is going to be allocated. So any images, text-fields, etc. Now, when you're done with that frame and move onto the next--assuming you have set everything on the previous frame to null--it should all disappear. Visually, it does, but just like the aforementioned "ship", all the elements are still in memory until the Flash GC nukes them.

So, now, imagine this:

Frame 1: Splash screen
Frame 2: Game Screen

Frame 3: Level Complete screen

Frame 4: Game Over Screen


The game starts and we show Frame 1 for a couple of seconds and then jump over to Frame 2. Frame 2 starts up your game with all its nifty little sprites and such. You play through the level and win (yay!). Now the game moves on to Frame 3 to show you how you did, and asks you if you're ready for the next level. Stop...

1 - Show Frame 1 (Splash)

2 - Show Frame 2 (Game)


3 - Remove elements from Frame 2


4 - Show Frame 3 (Level Complete)


5 - Remove elements from Frame 3


6 - Go back to item #2 in this list


See a problem here? All of the elements in Frame 2 are still sitting in memory (unless Flash's GC cleaned them up...highly unlikely), and then Frame 3's are as well. We could be 20 levels into a game and ALL of the prior data is still sitting in memory. UGH!

And this doesn't even touch the fact that if you have any event listeners tied to these items they'll keep firing until the GC deletes them. Even if you remove the even listener, the reference will stay there unless you set the useWeakReference to true.

When creating an event listener, instead of:
SayAgainButton.addEventListener(MouseEvent.CLICK,SayAgain);
Use:
SayAgainButton.addEventListener(MouseEvent.CLICK,SayAgain, false, 0, true);
Again, it'll still hang out until the GC picks it up, but at least it *will* pick it up eventually.

Until then, guess what happens? Yep, all the stuff you created from levels 1-19 will still be in memory firing until the GC snags it. Why? I honestly don't know. It seems rather silly to me.

Oh, one more thing to note: Even if you create a symbol that has a bunch of frames sitting inside of it, and you're using gotoAndStop(...) or next() to pulse through, etc., you'll see your memory go up. This is because it's not until you actual hit a particular frame that the frame is loaded. This is actually understandable and fine, but it is something to note as you're doing stuff so you don't pull you hair out.

So what is there to do?

Well, that's the $1M question. What I'm going to *try*, is the following:

1 - Remove the whole frame concept entirely.

2 - Create all my assets at the start of the project


3 - Instead of removing them from the child list and nulling them, I'm going to create a little (throw me off the screen) function that effectively does the same thing, but only retains ONE element in memory through the life of the game, no hundreds. I'll just stick the items at -999,-999 or something.


4 - I already reuse the majority of my elements in arrays, but it seems the child list is a part of the problem (at least from my testing). So I want to have a fixed set of addChild calls instead of numerous ones. Again, this is accomplished by creating the elements at the start of the game and reusing them throughout.


Will this solve the problem? That's the question I hope to answer tomorrow.

4 comments:

  1. I'll translate:

    Bad is bad, good is good.

    bad, bad.

    good, good.

    good, bad;

    bad...good...

    bad.

    ReplyDelete
  2. Can't you just override or manually initiate the GC in AS3?

    ReplyDelete
  3. Wouldn't that be awesome? There is supposedly a function that has that precise job, but it doesn't work.

    ReplyDelete