Fork me on GitHub

Memory management in Flixel

flixel-memory

I was helping a fellow out in the Flixel forums with memory management before I realized this issue is serious enough to warrant a proper blogging. I hope Adam Atomic (creator of Flixel) will take note and include these fixes in the main branch (I posted a new issue on Flixel’s github). This information is also for other Flixel programmers who are deeply familiar with the framework and not afraid to get intimate with it.

The problem with Flixel’s memory management system is that it doesn’t exist. However, every time you switch states, it does call destroy() on almost every object. The problem is that most of the destroy() functions are completely empty. Obviously, this is not enough.

UPDATE: I got onto github and forked flixel, wrote a patch, and did a pull request. I can’t solve the biggest problem though, which is that FlxQuadTree is being completely recreated on every frame. This is extremely terrible practice and causing the Flash GC to bust ass. I don’t use FlxQuadTree and I recommend building a persistent static grid to handle collisions instead (MUCH simpler and faster than quadtrees anyway). I’m pretty sure the quadtree is causing a huge performance hit alongside the memory thrashing, since my games don’t experience what I see in vanilla Flixel. Good luck!

You will need to go through every class yourself and override (or fill in) the destroy() method to nullify any significant variable in the class itself. Remember to call super.destroy() so that it chains through all super-classes too. Here’s what the FlxObject.destroy() should look something like:

public function destroy():void {
  velocity = acceleration = drag = maxVelocity = null;
  origin = scrollFactor = null;
  _point = null;
  _rect = null;
  _flashPoint = null;
  colHullX = colHullY = colVector = colOffsets = null;
}

Don’t actually use the code in this post–none of it compiles without errors, it’s merely an example (I will be submitting a tested patch for this whole thing to the trunk soon). Also, I’m using the latest build I got from the Flixel homepage–v2.43.

What I’m showing here is pretty straightforward. It just nullifies all object variables so the Flash GC will pick it up. We do the same thing in FlxSprite.destroy(), but we’re going to have to add the function ourselves because for some reason it doesn’t exist. So put this function at the end of FlxSprite:


override public function destroy():void {
  super.destroy();
  offset = scale = null;
  _curAnim = null;
  _callback = null;
  _flashRect = _flashRect2 = null;
  _flashPointZero = null;
  //_pixels.dispose(); // only do this if you don't use FlxG.addBitmap()
  //_pixels = null;
  _framePixels.dispose();
  _bbb.dispose();
  _framePixels = _bbb = null;
  _ct = null;
  _mtx = null;
  for (var i:int = 0; i < _animations.length; ++i) _animations[i] = null;
  _animations.length = 0;
  _animations = null;
}

So those are the first classes to hit. But there are dozens more. Other ones you should get to include (but not limited to) FlxQuadTree, FlxGroup, FlxButton, FlxEmitter, and FlxTilemap. There are a bunch more in flixel.data that you need to do too, in addition to adding destroy code to FlxGame itself.

FlxG._cache and what it does

An important thing to note is FlxG._cache. Whenever you create a new sprite, it stores the bitmapData in that Object. Adam’s reasoning for this was so that only one instance of the BitmapData object remains in memory, so new sprites just reference that one instead of creating new instances. I think this is a pretty solid idea, but I’m not sure how much it actually helps. If anyone has tested this, please post your results in the comments.

Anyway, if you loop through it like I did with FlxSprite._animations though, you will get a ton of null reference errors because you already nullified most of it by disposing of _framePixels in FlxSprite. I recommend adding a new destroy function to FlxG itself and going through _cache and running dispose on each object regardless (IF it’s BitmapData AND not already null)… just to be sure. Don’t forget to null the _cache Object itself.

Cleaning up your own mess

It is very important to override destroy() in ALL of your custom classes to nullify any new variables you added in them. In every class you create, add a destroy() override that also calls super.destroy() in order to ensure that the GC picks up everything. Don’t forget to remove any event listeners as well!

So that’s the memory problem in Flixel and how to fix it. Please retweet this post and share it with others. I think this is an important issue that needs to address in a future version of Flixel, as others are always finding out about it and posting their troubles in the forums. Let’s do our part to keep Flash from crashing everyone’s browser ;) Thanks!

SHARE THE LOVE

11 Comments on "Memory management in Flixel"

  1. Ryan Malm March 1, 2011 at 4:54 pm -

    I think this is another case of Adam assuming the end-user should be responsible for memory management and the code being left in the most beginner-user-friendly state as possible. Flixel is a framework meant to speed up the process of game development, NOT a neat and tidy all-inclusive game engine. Similar to your rant about unnecessary method calls and manual inlining on the forum, this seems like alot of arm-flailing and shouting “Look here! I found some stuff Adam didn’t do very well!” and not much real help. Truly helpful to the community would be to post your high-performance super-optimized build of flixel for everyone to pick-apart.

    • Corey March 1, 2011 at 5:37 pm -

      There are cases that would be considered “arm-flailing” but basic memory management is not one of them. All frameworks must provide some basic optimizations (especially memory-related). I don’t know what part of the programmery world you come from, but this is historic fact. Otherwise, Flash can (and notoriously has) crash a user’s browser.

      A ton of programming newbies use Flixel and are completely unaware that they have to handle these things themselves until their game suddenly becomes unplayable (as was the case with the forum post that started this). Obivously they must learn to manage the code they write, but there is no excuse to not have Flixel clean up after it’s own mess.

      Honestly, you’re contradicting your stance by saying it should be “left in the most beginner-user-friendly state as possible” while also suggesting that every user handle Flixel’s memory problem themselves. It is very not “beginner-user-friendly” to force these users to fix Flixel. Seriously, this is not a game/user-specific issue, but a fundamental problem in Flixel itself. Adding code like I wrote in the post will not make the game less user-friendly, rather it will accomplish the opposite by taking the matter off the users’ hands, leaving them to only worry about their own code. That’s the point of a framework dude.

  2. Colin Alteveer March 1, 2011 at 6:15 pm -

    No framework has to provide anything it doesn’t want to. You MUST take what you get, and if you don’t like it, make it better; the source code it available. The community would very much love for you to patch these problems, if you are willing.

    • Corey March 1, 2011 at 6:38 pm -

      Flixel should handle everything it… handles. You know? It creates sprites, it should destroy them too. Common sense/courtesy/community responsibility, call it whatever. Adam bent over backwards to make it extremely easy to use–and very feature-rich–so I see this issue as a simple oversight on his part.

      I wouldn’t mind writing a full patch for this as soon as I get familiar with github. Just signed up today, have no idea how this (or any other open source thing) works.

  3. Richard Davey March 1, 2011 at 10:57 pm -

    I think the key part of this post is disposing of bitmap data and the cache. And of course any persistent references such as events. Although to be fair Flixel doesn’t use events, so you would have had to add them yourself – and you should have done so with weak references/stops in mind.

    I disagree that *all* variables need nulling though. I’ve never seen any evidence to support the claim that GC may not run if say an integer has a value in it (for example maxVelocity). To be fair bitmapData’s have no strong references either, and once the parent is no longer linked they are valid for gc without any further work. But it makes total sense to control when this happens, disposing of them when you need it, not at some random avm determined point in the future.

    • Corey March 2, 2011 at 12:32 am -

      Right, the GC will pick up those variables as long as nothing else is referencing them. But that’s just it–sometimes we leave a reference behind, forgetting to null the link. That’s why I think it’s important to always nullify any object variables, even though there’s no link to them now–it will help other developers out if they create a reference but don’t clean it up later. With all the programmer newbies that Flixel attracts, this will happen. It’s also just good coding practice.

      It should be noted that Flixel uses quite a lot of events in FlxGame–not just for input, but the focus events as well. None of these events are removed, nor are they set use weak references either.

  4. pixelrevision March 16, 2011 at 4:15 pm -

    Have you figured out what is causing this? Apparently the flex profiler is now for some upgraded version to what I have. I am using an object pool for all of my assets, and am still seeing my memory slowly grow as the level progresses using add() on groups.

    • Corey March 16, 2011 at 4:42 pm -

      FlxGroup is always the first thing to go when I start a new Flixel-based project. It has some convenient functions, but it is probably the biggest performance hitter of them all. Anyway, I don’t have Flash Builder or a Flex profiler (that I know of–I use FlashDevelop), so I’ll need help testing my patch–please email/message me if anyone is interested.

      I haven’t started to dig in yet, but I’ll be posting on the Flixel forums when I have something.

  5. Tsweston August 22, 2012 at 12:12 pm -

    > I can’t solve the biggest problem though, which is that FlxQuadTree is being completely recreated on every frame. This is extremely terrible practice and causing the Flash GC to bust ass. I don’t use FlxQuadTree and I recommend building a persistent static grid to handle collisions instead (MUCH simpler and faster than quadtrees anyway). I’m pretty sure the quadtree is causing a huge performance hit alongside the memory thrashing, since my games don’t experience what I see in vanilla Flixel. Good luck!

    I’m aware this post is over a year old but I’m having some relevant problems. I’m currently using flixel to create some games for mobile, and I’ve found that FlxQuadTree is causing the GC to go absolutely crazy. If you still have it, could you possibly share your collision code with us?

    • Corey August 26, 2012 at 5:25 am -

      There’s examples of static grids all over the web though. Here’s Grant’s, which is nice and simple: http://gskinner.com/blog/archives/2008/01/proximitymanage.html

      And you can do cool stuff like raycasting with them too: http://dev.mothteeth.com/2011/11/2d-ray-casting-on-a-grid-in-as3/

      The one I use is too tied up with the rest of my engine to release as a general utility. I modeled it after what this guy did: http://www.kirupa.com/forum/showthread.php?350553-Rumblesushi3D-Collision-Grid-demo

      Hope this helps. Good luck.