Remember back when you learned C? Way, way back in time? Well I do. When I recall my experience with C, I remember it being intoxicatingly fast and powerful. Kinda felt like mainlining into an artery of the machine: so much potential for incredible speed, but also horrendously dangerous and unforgiving.
For a coding noob, C’s requirement for manual memory management is perhaps the most time- and brain-consuming ‘feature’. Writing anything but the most basic of programs (i.e. using only “stack” memory) required consistent focus on the task of correctly allocating, managing, and deallocating memory. Accidentally accessing data from a part of memory that you hadn’t allocated resulted in that familiar spartan sting: Segmentation fault. No fancy stack trace, elaboration, or even an apology. The machine just smugly states how you fucked up and dumps you back to the shell. I remember an amusing graffiti on the computer lab’s whiteboard: “it’s all seg’s fault!”
Since I’d just come from a Java background the previous year, I found this “memory stuff” initially very frustrating and intimidating. Though I didn’t write off C as “just a stupid or difficult language” because I firmly believed that this overhead must be a tradeoff for some other benefits which I had yet to discover or fully appreciate.
Although I learned later that there was a debugger (gdb) that took away a lot of the pain of seg faults, it didn’t make the task of managing memory any easier: it was just helpful when things went wrong. The debugger doesn’t help, for example, with memory leaks. If you repeatedly allocated memory but forgot to deallocate it, even a small, simple program could eventually end up hogging a very large amount of memory.
As time went on, I got comfortable with managing memory, but I never forgot about my assumption that there were something(s) so amazing about the language that balanced up the overhead for manual memory management.
And to be fair, C is an inately quick language, which is fairly obvious from the start. This is mostly because it’s pretty much the CPU’s roommate: CPUs and the C language have evolved together over the last few decades, and they’ve formed a pretty formidable partnership. But also, some features of C (e.g. memory pointers) open up algorithmic possibilities that don’t really have a comparable equivalent in other managed languages, like Java.
Memory Management as a Focus Burglar
Having said this though, I remember the day that I decided “this isn’t worth it”. I had grown to really enjoy aspects of memory management and the associated benefits, but for me, it just wasn’t worth the significant overhead of time and focus. The tasks of manual memory steal focus from the programmer, and make it more difficult to concentrate on the actual problem at hand.
So since then, I’ve hardly touched any unmanaged languages. I’ve had many arguments about whether or not this is a bad thing, but in the end I’m pretty happy with it. So there.
I’ve relatively recently taken an interest in Flash, mostly because of its relatively new, strongly-typed, compiled, managed language: ActionScript 3. It’s a super groovy language, with a decent community and lots of sweet libraries (some of which have been ironically ported from C and C++). Although its previous version was a lot like JavaScript, AS3 is more akin to Java than any other language. AS3 makes conventional, structured application development possible.
And, since the world seemed to be moving inexorably toward managed languages, I thought that I had left the world of memory leaks in the archaic past, and that it would henceforth be dredged up only in drunken thought experiments with old-school hackers in dingy bars at 4am.
Not ’til the Fat Lady sings
But I was wrong. Because addEventListener() is the new malloc().
If you repeatedly (or naïvely) add event listeners but forget to remove them, even a small, simple flash application can eventually end up hogging a very large amount of memory. Sound familiar?
Typically this happens when an developer creates load listeners, or mouse listeners, then removes assets from the display list without removing the listeners. Those assets still remain in memory as long as they are “listened to”. This is because the listener (e.g. the stage) retains a list of all of its dispatchers, so the listener can’t be garbage collected until it is removed from this list. Using “weak links” when creating adding listeners can help, but I have historically found this too buggy to rely on.
In ActionScript 2, although this mechanic was also possible, it manifested itself less often, because Flash assets were removed from all dispatcher lists (and from memory) when they were removed from the display list using unloadAndStop() or similar. In AS3, however, assets can exist before or after they’ve been removed from the display list, which is less forgiving for sloppy coding.
This of course, isn’t specific to ActionScript: just any managed language which includes or supports event dispatchers and listeners (C#, Java, etc.). Extra care needs to be taken when using this model. Everytime you add a “transient” listener, there should be code somewhere else to remove that listener, just like malloc/dealloc.
Here’s some psuedo-as code to demonstrate:
loader.addEventListener(COMPLETE, onLoadComplete);
function onLoadComplete() {
loader.removeEventListener(COMPLETE, onLoadComplete);
// ... (Loader handling code)
}
So that’s pretty much it.
Tags: Flash and ActionScript