The Memory Pit

So I know I was supposed to do a post on how notes get distributed on the virtual staff, and I will get to that. I just wanted to post a quick update and talk about whats been going on with the project lately.

One of the ideas that I put forth when describing the project is that a stream is equivalent to an "endless album". It should loop through the same progressions indefinitely but should spin up new sets of notes with each iteration. Full disclosure - I discovered a bug in the plugin in late January and realized that wasn't happening. The same notes where being spit out with each cycle, the only difference in the resulting audio being the different presets that happened to be loaded in my synths at the time.

The fix for that was easy enough. It basically amounted to rearranging a few blocks of code. However, making this change exposed an enormous memory leak in the plugin. This one was actually more like a memory pit - with each iteration through the composition lists, memory allocated for certain objects (most notably compositions, progressions and notes) basically doubled, along with the object counts. Why?

To get to the bottom of this, I fired up dotTrace memory profiler. I'm not too used to dealing with memory leaks in .NET - generally garbage collection will release resources for all eligible objects and I am pretty careful not to create objects willy-nilly. Point being, it took me a little while to figure out what exactly I was looking at and how to really go about diagnosing the leak. The view that made the most sense to me was the root path, as that was the easiest way for me to visualize where the references to old objects where originating from. The following two images are screenshots from iterations 2 and 3 of an Aleator run:

 Iteration 2

Iteration 2

 Iteration 3

Iteration 3

I know the images are small - I am looking specifically at the memory allocated on the heap for Composition objects. As you may or may not be able to see, there were 5 compositions in the second node during iteration 2, but 10 for iteration 3. There are only supposed to be five compositions in memory for Facets, so basically we have a smoking gun here.

As it happens, I lucked out with respect to a solution. Digging down further into the node, we can see that old notes have references in a list called m_isPlayingList. This was actually a holdover from a time when I wanted to keep track of currently playing notes so I could turn them off if they were hanging after a progression or composition switch. That is no longer needed, since at this point "all notes off" messages are sent on all channels except for drums if the progression is changing. I was able to simply remove all references to this list and that in and of itself fixed the leak. The fact that notes hold references to their parent progressions and compositions meant that none of these objects were ever eligible for garbage collection. Ugh. Since the leak was causing intermittent OutOfMemory exceptions, I am hopeful that this fix will result in a little more stability for the stream.

At the end of the day, I guess this is really an advertisement for JetBrains dotTrace. If you run into trouble with memory or performance during your dev adventures, it's a pretty nice tool...