views:

117

answers:

2

I have a bit of a memory leak issue in my Flex application, and the short version of my question is: is there any way (in AcitonScript 3) to find all live references to a given object?

What I have is a number of views with presentation models behind each of them (using Swiz). The views of interest are children of a TabNavigator, so when I close the tab, the view is removed from the stage. When the view is removed from the stage, Swiz sets the model reference in the view to null, as it should. I also removeAllChildren() from the view.

However when profiling the application, when I do this and run a GC, neither the view nor the presentation model are freed (though both set their references to each other to null). One model object used by the view (not a presenter, though) IS freed, so it's not completely broken.

I've only just started profiling today (firmly believing in not optimising too early), so I imagine there's some kind of reference floating around somewhere, but I can't see where, and what would be super helpful would be the ability to debug and see a list of objects that reference the target object. Is this at all possible, and if not natively, is there some light-weight way to code this into future apps for debugging purposes?

Cheers.

+2  A: 

Flash GC uses a mix of ref counting and mark and sweep, so it does detect circular references. It seems rather you're having another reference in you object graph. The most common reason is, that the objects you want disposed still are having event handlers registered on objects that are not disposed. You could try to ensure that handlers are always registered with weak reference. You could also override addEventListener and removeEventListener in all (base) classes, if possible, to look which listeners are registered and whether there are chances for some not to be removed.

Also, you can write destructors for your objects, that for ui components clear graphics and remove all children, and for all objects, removes references to all properties. That way, only your object is kept in RAM, which shouldn't require much memory (a small footprint of 20 B or so, plus 4 B per variable (8 for a Number)).

greetz
back2dos

back2dos
Thanks back2dos. I tried putting in a dispose() method of my view to ensure the model was compeltely separated, to drop all the children and to unhook any (obvious) listeners, but no luck :(. Well, actually I think it works a little better (the view is only not cleaned up sometimes), but the leak persists.I tracked the addEventListener calls in the view with the override (more or less what I was after, thanks), and there were 9 adds (2 weak) and 1 remove, so there could be something there. It's mostly from MXML data binding though (except for a 'created' and 'removed'), so I'm not sure.
Pie21
+1  A: 

Assuming you are using Flex Builder, you could try the Profiler. In my experience, it's not so good for profiling performance, but it's been of great help for finding memory leaks.

It's not the most intuitive tool and it takes a while to get used to it (I mean, to the point where it actually becomes helpful). But, in my opinion, investing some time to at least learn the basics pays off. There's an enormous difference between just seeing how much memory the player is using globally (what System.totalMemory gives you, a very rough, imprecise and often misleading indicator) and actually track how many instances of each object have been created, how many are still alive and where were they allocated (so you can find the potential leak in the code and actually fix it instead of relying in black magic).

I don't know of any good tutorials for the FB profiler, but maybe this'll help to get you started.

First, launch the profiler. Uncheck performance profiling and check everything else (Enable memory profiling, watch live memory data and generate object allocation stack traces).

When the profiler starts, you'll see stats about the app objects, grouped by class. At this point, you might want to tweak filters. You'll see a lot of data and it's very easy to be overwhelmed. For now, ignore everything native to flash and flex stuff, if possible, and concentrate on some object that you think it should be collected.

The most important figures are "cumulative instances" and "instances". The first is the total number of instances created so far; the second, the number of said instances that are still alive. So, a good starting point is get your app to the state where the view you suspect that leaks gets created. You should see 1 for "cumulative instances" and "instances".

Now, do whatever you need to do to get to the point where this view should be cleaned up (navigate to other part of the app, etc) and run a GC (there's a button for that in the profiler UI). A crucial point is that you will be checking the app behaviour against your expectations -if that makes sense-. Finding leaks automatically in a garbarge collected environment is close to impossible by definition; otherwise, there would be no leaks. So, keep that in mind: you test against your expectations; you are the one who knows the life cycle of your objects and can say, "at this point this object should have been collected; if it's not, there's something wrong".

Now, if the "instances" count for you view goes down to 0, there's no leak there. If you think the app leaks, try to find other objects that might not have been disposed properly. If the count remains at 1, it means your view is leaked. Now, you'll have to find why and where.

At this point, you should take a "memory snapshot" (the button next to the Force GC button). Open the snapshot, find the object in the grid and double click on it. This will give you a list of all the objects that have a reference to this object. It's actually a tree, and probably each item will contain in turn a number of backreferences and so on. These are the objects that are preventing your view from being collected. In the right panel, also, you will an allocation trace. This will show how the selected object was created (pretty much like a stack trace).

You'll probably see a hugh number of objects there. But your best bet is to concentrate in those that have a longer life cycle than the object you're examining (your view). What I mean is, look for stage, a parent view, etc; objects on which your view depends on rather than objets that depend on your view, if that makes sense. If your view has a button and you added a listener to it, your button will have a ref to your view. In most cases, this is not a problem, since the button depends on the view and once the view is collect, so is the button. So, the idea is that since there are a lot of objects, you should try to stay focused or you will get nowhere. This method is rather heuristic, but in my experience, it works.

Once you find the source of a leak, go back to the source, change the code accordingly (maybe this requires not just changing code but refactoring a bit). Then repeat the process and check whether your change has caused the desired effect. It might take a while, depending on how big or complex is your app and how much you know about it. But if you go step by step, finding and fixing one problem at the time, you'll eventually get rid of the leaks. Or at least the worst and more evident ones. So, while a bit tedious, it pays off (and as a nice aside, you'll eventually understand what a waste of time is in most cases to use weak refs for every single event handler on the face of this earth, nulling out every single variable, etc, etc; it's an enlightening experience ;).

Hope this helps.

Juan Pablo Califano
Wow, thanks a bunch. That double-clicking the memory snapshot is exactly what I was after. The visualisation is rather convoluted, unfortunately. I have 22 back references, composed of a whole lot of UIComponentDescriptors (which I imagine are children holding a parent reference?), a few Functions (seem to represent listeners?), a couple of Bindings and PropertyWatchers, but not anything that is obviously *another* component. A couple of the Function items have a Button child (with a property like [listener1]), so I'm guessing they're the click event listeners I defined in the MXML definition.
Pie21