Memory leak when hosting vst3 plugin instances

I started experimenting with hosting plugins within a juce app which works very nice. I can add a VST3 plugin instance to a audio graph and display the editor in a separate window. When I close my application, however, the LeakedObjectDetector complains about a leaked instance of VST3HostContext. Looking a bit deeper I found a potential bug in VST3ComponentHolder which creates a new VST3HostContext instance in its constructor and never actually deletes it. In its destructor it just assigns nullptr to the corresponding pointer object, which is wrong in my opinion. But maybe I’m just doing the whole plugin hosting wrong and the leak is on my side, I don’t know. If I explicitly delete the instance in the VST3ComponentHolder destructor the error disappears. Can anyone please take a look and tell me if my assumption is correct? Working on mac by the way, if this is relevant. Thank you!

Did you borrow any of your implementation from the Audio Plugin Host project in the repo? that’s what you should be following for how to host a plugin.

Yes, my implementation is inspired by the example project, but less complex. For example I hard coded the file to be loaded and did not implement the plugin selection window.

I just checked with the mentioned example where the same memory leak is indicated. I am convinced now that this is a bug in VST3ComponentHolder. However, I don’t know what’s the most elegant way to fix it. Just typing delete host; in the destructor does not seem right.

It’s not a mistake. host is a COM smart pointer so is ref-counted.

Most likely would be that you’re leaking an object yourself which indirectly keeps a reference to that object, but haven’t got a leak detector on your own object to spot that.

As I said, the example project behaves exactly the same. But it seems like this happens only with certain plug-ins, e.g. it happens with RoughRider2, so maybe it’s a false alarm. But you are completely sure that the nullptr assignment in ~VST3ComponentHolder() is correct? When is the object actually destroyed?

Yes!

Yeah, some plugins may implement the ref-count differently/incorrectly and have an out-by-one-error. We’ve seen that kind of thing happen before. But I wouldn’t worry about it (other than taking this as a hint that maybe it’s time to learn about smart pointers!)

Ok, thank you! I know a bit about smart pointers, I just did not see where in the implementation of ComSmartPtr the pointed to VSTHostContext object is destroyed if we assign nullptr before. I still don’t see it to be honest, but I will concentrate on more important things now :wink:

@jules Could you please help me understand. In the d’tor of ComSmartPtr I read if (source != nullptr) source->release();. But source is actually nullptr due to the preceding assignment. In my understanding the ref counter is not decreased and the object is never destroyed. Where is my mistake?

Assigning any new pointer to a smart pointer will release the old one. But seriously, you’re stuck in the weeds, there’s no bug in the pointer handling!

ok, thanks.

Hello,
digging up this thread as I’m creating a DAW, and getting into this issue.
While it’s not a “big” deal, this makes it very inconvenient when testing the software to always have memory leak notices when closing, especially when you’re looking for potential other memory leaks.

Anyway, I found a workaround to force cleaning VST3HostContext pointers even if the plugin is not properly doing it. IMHO, handling it on the JUCE side is the only realistic way to deal with this issue, as the other solution would be to contact all VST devs and tell them to modify their code…

I’m not putting a Pull Request on the JUCE github as the PR list is giant and my previous PRs have still not been processed.
Also, this is a bit dirty and I’m sure jules will have plenty of remarks before this is getting proper enough for official JUCE code :slight_smile:

juce_VST3PluginFormat.cpp, line 3320 :

    VST3HostContext* context = new VST3HostContext();
    int numRefAfterDelete = 0;
    {
        ComSmartPtr<VST3HostContext> host(context);
        DescriptionLister lister(host, pluginFactory);
        lister.findDescriptionsAndPerform(File(fileOrIdentifier));
        results.addCopiesOf(lister.list);

        numRefAfterDelete = host.releaseAndGetRefCount();
    }

    if(numRefAfterDelete > 1) delete context; //ref should only be one, otherwise this will lead to a memory leak

And added the method ComSmartPtr::releaseAndGetRefCount() in juce_VST3Common.h

int releaseAndGetRefCount()
{
    int result = 0;
    if (source != nullptr) result = source->release();
    source = nullptr;
    return result;
}

While I don’t think this is the real way to handle it, getting a method to know the ref count of a ComSmartPtr’s source can be interesting to check this kind of behaviour and ensure proper cleanups.

2 Likes

Would you happen to have this available somewhere for the newer version of JUCE?