AudioKit: Possible Memory Leak in Reverb Implementations

I have a relatively complex application which builds songs in memory dynamically using various pieces of the AKSequencer. In doing this, each song builds up its own signal chain with channels and effects. When songs are changed, everything gets torn down and rebuilt. This has been working really wonderfully, and everything is clean.

However, today I noticed that as I changed songs many times, the memory usage of my app in XCode was increasing each time. Using the Instruments tool and the leak identifier built into that, I was able to isolate what I believe may be a memory leak in AudioKit itself, specifically in the Reverb implementations.

I have attached a couple of images from the Instruments tool to show what I’m seeing. I have narrowed them down to only show the relevant parts.

Screen Shot 2020-09-03 at 9 33 47 PM Screen Shot 2020-09-03 at 9 34 37 PM

“DrumsTrackDefinition” is my own code. I have included the second image to show that I’m seeing this in three different Reverbs, with the AKZitaReverb seeming to be the worst offender w/r/t amount of memory leaking.

Here is what the “createRoute” method shown there from my code does. As you will see, it is straightforward:

    func createRoute(from signalNode: AKNode, to mixerNode: AKMixer) -> [AKNode] {
        
        var effectNodes = [AKNode]()
        
        let reverb: AKNode
          
        switch genotype.reverbImplementation {
        case .chowning:
            reverb = AKChowningReverb(signalNode)
        case .costello:
            reverb = AKCostelloReverb(signalNode)
        case .appleReverb:
            reverb = AKReverb(signalNode,
                              dryWetMix: 1.0)
        case .appleReverb2:
            reverb = AKReverb2(signalNode)
        case .zita:
            reverb = AKZitaReverb(signalNode,
                                  dryWetMix: 1.0)
        }
        effectNodes.append(reverb)
        
        let dryWetMixer = AKDryWetMixer(signalNode, reverb, balance: genotype.reverbMix)
        effectNodes.append(dryWetMixer)
        
        let localMixer = AKMixer(dryWetMixer)
        localMixer.volume = Volume(userValue: 0.7).mixerValue
        effectNodes.append(localMixer)
        
        localMixer >>> mixerNode

        return effectNodes
    }

The reason I do it this way is so that I can maintain a reference to the “effectNodes” elsewhere for later deallocation. So all that’s happening here from my code’s side is just instantiating these nodes, and they are being identified as memory leaks. (FTR, I use this exact same technique in many other places in the same product, and there are no leaks identified, but I only use the Reverb nodes on the Drums, so that is why I think it’s being identified only here.) I don’t know exactly how Instruments is able to tell this is a memory leak, but I assume it must be somehow noticing that no reference is being kept to the allocated memory, which means it cannot be deallocated later? I don’t know.

Later, I do cleanup of the nodes like this, but it is not deallocating this memory. Hence the leak.

    deinit {
        for node in nodes {
            node.disconnectOutput()
            node.detach()
        }
    }

I will be happy to provide you any additional information that you might need or want to help debug this. Please let me know.

Thank you!

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 22 (21 by maintainers)

Most upvoted comments

I made myself a little example project and proved that the deinit was being called, but memory was still leaking. Paul might be onto something in that its possible init is being called twice and deinit only once.

Thanks for this, we should be able to track this down.