I’m oh-so-close to posting a new update to Bowmaster Winter Storm (Alpha 0.0.1.9). I was so ready to start preparing the files for post and I blocked out an entire day to work on it. As I started working I discovered, much to my dismay, a memory leak! Dun dun dunnnnn! Well, I noticed that after performing some overnight tests using never ending battles that the game would use up all available memory and then crash the web browser… err…maybe I shouldn’t be telling you this.
To those of you who don’t know what a memory leak is it’s when a computer application starts using up memory but doesn’t free it up after it’s done using it. This is most often the developer’s fault due to a programming bug or just poor design. A simple flash example of how this might occur with poor design is that let’s say every time you killed a unit or closed a window that instead of removing it from memory the programmer coded it to just turn invisible by setting alpha = 0. Essentially this would cause the data to continue to exist in memory instead of being freed for use for new enemies and new windows. Unfortunately my memory leak was caused by something way more difficult to locate.
Anyway, it’s quite demoralizing when you set aside an entire day to work on play-testing and game balancing (the fun stuff) to then discover that you first need to fix a major issue instead. Not a great way to start out the day. Looking at the faded out crashed internet explorer window with a frozen image of bowmaster carnage in the background, I was not looking forward to hacking up my code to find the cause behind the memory leak. Image having an apartment that you just cleaned and organized and then getting a knock at the door – It’s the FBI and they have a warrant to search the place. Sure it has to be done but in so doing they’re likely going to trash the place. That’s what debugging memory leaks can be like. I just got done working on my unit-skinning system and re-integrating old units and adding new ones. Now I need to go into the code and start turning off stuff, add hard coded test functions to inspect output, and otherwise break the game in order to try to fix it.
Not knowing how long this was going to take or even where to start for sure, I did my best to divide and conquer. Unfortunately it’s not easy just turning off half the code without adding new bugs, but in theory if you could turn off areas of the program in half increments you can potentially narrow down the source of the problem fairly quickly. For example, if I suspected that some of the new unit skins may have been causing the problem, I could just not load those units and only load the ones that I know have worked in the past.
Not only is it difficult to find a memory leak, it may be difficult to replicate one in a short period of time because it may take a while before the impacts are noticeable. I needed a way to accelerate the process of replicating the issue. I hacked my game to load tons of units in random areas of the map so they would instantly engage in battle. With more data being loaded and unloaded more frequently I could now inspect the memory usage using the Windows system processes inspector to see the memory increase quicker. I was also able to get the web browser to crash in a matter of minutes instead of hours. Yay!
It may sound odd to be happy to cause the game to crash, but when debugging it’s essential to be able to recreate the issue. With this step complete I was ready to start turning off sections of my code to see if doing so would remove the issue. The usual suspects for potential memory leakage were the new units, the elaborate special death animations (slice in two, electrocute etc.), and certain custom units like pikemen and dragons that have special ways in which they attack.
Technically speaking, there are few known best-practices that will prevent memory leaks from occurring. If you’re not a programmer or you do not care about these tips then feel free to skip down to where I talk about how dumb I am.
1. Use weak references when adding event listeners.
addEventListener(“eventString”, listenerFunc); // wrong way
addEventListener(“eventString”, listenerFunc, false, 0, true); // correct way. The last three parameters are optional but the default useWeakReference parameter is set to false so you need to set it to true.
2. Even if you use weak references, call removeEventListener to explicitly remove event listeners when you know they aren’t being used.
3. Adding code to clips embedded in a timeline may cause memory issues. For example if you add a listener to a clip on the timeline and then that clip is replaced by a blank keyframe you may have memory problems. Another example is if you give a clip on the timeline an instance name and then move that clip to a different layer with the same instance name you may have memory problems.
4. Remove or nullify references to unused objects. An object will continue to stay in memory if there are references to it. In Bowmaster for example, I keep a singleton reference to the LevelManager class instance for easy global access in other classes. However, this instance is only relevant when the player is in-game and is not relevant when viewing the main menu screen. Therefore I need to make sure to set the global static singleton variable to null when the game window is closed otherwise all objects still referenced by the LevelManager will not be freed.
How dumb I am…
So despite how smart I may sound listing all those tips above, I felt like a total flash noob spending the entire day trying to locate the problem. I kept turning on and off code and checking the results but every time I wasn’t able to specifically pinpoint the issue. I thrashed for hours, 12 full hours, trying different things and documenting the results. At some point you just need to call it a day, and I probably wasn’t very productive or efficient for those last four hours I worked, but it’s hard to stop when you think to yourself, maybe if I try just one more thing…
The next couple of days I was forced to only work in short increments, but I was making progress. I eventually turned off all the units except the grunt and started testing one skin at a time. I discovered that just one of the five unique grunt skin sets was causing a memory leak. Guess which one… It was the orc. Yep, I created a new grunt unit that used the same basic animation as the skeleton foot soldiers but changed the graphics to look like an orc. And then for some reason, in an act of defiance, the orc decided to crash my game. I give him life and this is how he repays me? By hording all the memory and never giving it back? Bad orc! Bad! (::scrohboo::) So it wasn’t just the fact that he looked like an orc — that would just be silly. Specifically I narrowed it down to some embedded code that I used to animate the shine on the orc’s sword. What was unsettling was that I used this embedded code approach before in other animations. I’m thinking that the new skin redesign may have made this code incompatible, or maybe it was always broken and I just never noticed the memory leak until now.
Luckly, after some tweaking I was able to get the shine animation to work AND not crash the web browser (i.e. no memory leak), but I’ll need to inspect other areas of the game that use similar embedded code and fix those as well.
It was one of those traumatic software development experiences where there’s no hope and you wonder if you’re going to have to redesign the entire application… Yeah, maybe I blamed flash a little too. I was quite vexed about this issue. I did not want to crash people’s web browsers. I care about the user. Yeah, maybe the issue will only affect those that play for hours straight without refreshing the browser, but I’m not going to deny them that opportunity just because I didn’t want to fix a bug.
On one hand it looks as if it took several hours just to find and fix one bug. On the other hand, I just spent several hours towards the goal of ensuring that the user has an error free experience. And in the process of doing so I’ve implemented several useful debugging systems that will allow me to test the game much easier in the future.
Yeah, so maybe it’s not so easy to skin an orc. But in the end it will be worth it. Stay tuned. You’ll see.
UPDATE: Not so fast jase… don’t think this is over. Read Part 2.