Leaks in OSXTypeface and MemoryBlock

Hi there,

my first post here as I’m still quite new to the JUCE thing. So, my problem is that the juce leak detector marks leaked objects in the classes OSXTypeface, Typeface and MemoryBlock.

Bildschirmfoto 2023-11-08 um 12.32.54

I tried including the JUCE_HEAVYWEIGHT_LEAK_DETECTOR to OSXTypeface and Typeface and it spits out this stacktrace:

*** Leaked objects detected: 1 instance(s) of class OSXTypeface

Backtrace 1
-----------------------------------------------------------------
0   Auto_Sample                         0x000000010077d730 _ZN4juce11SystemStats17getStackBacktraceEv + 96
1   Auto_Sample                         0x000000010097ec94 _ZN4juce31HeavyweightLeakedObjectDetectorINS_11OSXTypefaceEEC2Ev + 32
2   Auto_Sample                         0x000000010097dd80 _ZN4juce31HeavyweightLeakedObjectDetectorINS_11OSXTypefaceEEC1Ev + 28
3   Auto_Sample                         0x000000010097dc30 _ZN4juce11OSXTypefaceC2ERKNS_4FontE + 252
4   Auto_Sample                         0x000000010090eee8 _ZN4juce11OSXTypefaceC1ERKNS_4FontE + 36
5   Auto_Sample                         0x000000010090ee84 _ZN4juce8Typeface23createSystemTypefaceForERKNS_4FontE + 44
6   Auto_Sample                         0x000000010090f474 _ZN4juce4Font25getDefaultTypefaceForFontERKS0_ + 572
7   Auto_Sample                         0x0000000100a2d030 _ZN4juce11LookAndFeel18getTypefaceForFontERKNS_4FontE + 296
8   Auto_Sample                         0x0000000100a2be3c _ZN4juceL33getTypefaceForFontFromLookAndFeelERKNS_4FontE + 48
9   Auto_Sample                         0x00000001009780f8 _ZN4juce13TypefaceCache15findTypefaceForERKNS_4FontE + 832
10  Auto_Sample                         0x00000001009053c8 _ZN4juce4Font18SharedFontInternal14getTypefacePtrERKS0_ + 100
11  Auto_Sample                         0x00000001008a8adc _ZNK4juce4Font14getTypefacePtrEv + 48
12  Auto_Sample                         0x00000001009069ec _ZNK4juce4Font19getStringWidthFloatERKNS_6StringE + 40
13  Auto_Sample                         0x00000001009069b0 _ZNK4juce4Font14getStringWidthERKNS_6StringE + 32
14  Auto_Sample                         0x0000000100aa2000 _ZN4juce11AlertWindow12updateLayoutEb + 104
15  Auto_Sample                         0x0000000100aa1b80 _ZN4juce11AlertWindow18lookAndFeelChangedEv + 156
16  Auto_Sample                         0x00000001009e4bc4 _ZN4juce9Component21sendLookAndFeelChangeEv + 60
17  Auto_Sample                         0x00000001009ec8fc _ZN4juce7Desktop21setDefaultLookAndFeelEPNS_11LookAndFeelE + 204
18  Auto_Sample                         0x0000000100a2cef4 _ZN4juce11LookAndFeel21setDefaultLookAndFeelEPS0_ + 40
19  Auto_Sample                         0x00000001004b4bdc _ZN26SaemplAudioProcessorEditorC2ER20SaemplAudioProcessor + 240
20  Auto_Sample                         0x00000001004b5058 _ZN26SaemplAudioProcessorEditorC1ER20SaemplAudioProcessor + 36
21  Auto_Sample                         0x00000001004b10f8 _ZN20SaemplAudioProcessor12createEditorEv + 44
22  Auto_Sample                         0x00000001006252c4 _ZN4juce14AudioProcessor20createEditorIfNeededEv + 104
23  Auto_Sample                         0x000000010041ba6c _ZN4juce22StandaloneFilterWindow20MainContentComponentC2ERS0_ + 264
24  Auto_Sample                         0x000000010041b934 _ZN4juce22StandaloneFilterWindow20MainContentComponentC1ERS0_ + 36
25  Auto_Sample                         0x000000010040dddc _ZN4juce22StandaloneFilterWindow13updateContentEv + 44
26  Auto_Sample                         0x000000010040dac8 _ZN4juce22StandaloneFilterWindowC2ERKNS_6StringENS_6ColourEPNS_11PropertySetEbS3_PKNS_18AudioDeviceManager16AudioDeviceSetupERKNS_5ArrayINS_22StandalonePluginHolder12PluginInOutsENS_20DummyCriticalSectionELi0EEEb + 468
27  Auto_Sample                         0x000000010040d7e8 _ZN4juce22StandaloneFilterWindowC1ERKNS_6StringENS_6ColourEPNS_11PropertySetEbS3_PKNS_18AudioDeviceManager16AudioDeviceSetupERKNS_5ArrayINS_22StandalonePluginHolder12PluginInOutsENS_20DummyCriticalSectionELi0EEEb + 132
28  Auto_Sample                         0x000000010040a7f4 _ZN4juce19StandaloneFilterApp12createWindowEv + 192
29  Auto_Sample                         0x000000010040a55c _ZN4juce19StandaloneFilterApp10initialiseERKNS_6StringE + 32
30  Auto_Sample                         0x00000001008670b0 _ZN4juce19JUCEApplicationBase13initialiseAppEv + 196
31  Auto_Sample                         0x00000001009cc77c _ZN4juce15JUCEApplication13initialiseAppEv + 24
32  Auto_Sample                         0x0000000100866e2c _ZN4juce19JUCEApplicationBase4mainEv + 300
33  Auto_Sample                         0x0000000100866ca8 _ZN4juce19JUCEApplicationBase4mainEiPPKc + 68
34  Auto_Sample                         0x000000010040a174 main + 56
35  dyld                                0x0000000180da5058 start + 2224

It appears that the leak happens when trying to paint the “Options” button when building my plugin in Standalone mode. Because I had a custom method for

Font getTextButtonFont(TextButton&, int buttonHeight) override

I removed my custom font and reimplemented the look and feel v4 version of that method to check if that resolves the problem:

Font getTextButtonFont(TextButton&, int buttonHeight) override
    {
        return { jmin (16.0f, (float) buttonHeight * 0.6f) };
    }

This didn’t solve my problem though and the stacktrace remains the same.

As for the MemoryBlock leak, I tried to include the heavyweight detector into the MemoryBlock class but this doesn’t work. Then I found this entry: https://forum.juce.com/t/using-juce-heavyweight-leak-detector-on-juce-classes/52930 and tried the trick that @oli1 mentioned there but this doesn’t work either because then it doesn’t want to build anymore.

The only times I’m using the MemoryBlock class in my code is in the plugin processor:

void SaemplAudioProcessor::getStateInformation (juce::MemoryBlock& destData)
{
    // You should use this method to store your parameters in the memory block.
    // You could do that either as raw data, or use the XML or ValueTree classes
    // as intermediaries to make it easy to save and load complex data.
    XmlElement stateInfo("Blome_StateInfo");
    XmlElement* stateInfoBody = new XmlElement("Blome_StateInfoBody");
    
    // Store navigation panel state
    String activeNavigationPanel = "";
    
    switch (mActiveNavigationPanelType)
    {
        case PANELS_LIBRARY_PANEL:
            activeNavigationPanel = "PANELS_LIBRARY_PANEL";
            break;
        case PANELS_TABLE_PANEL:
            activeNavigationPanel = "PANELS_TABLE_PANEL";
            break;
        case PANELS_MAP_PANEL:
            activeNavigationPanel = "PANELS_MAP_PANEL";
            break;
        default:
            break;
    }
    
    stateInfoBody->setAttribute("ActiveNavigationPanel", activeNavigationPanel);
    
    // Store sorting column title state
    stateInfoBody->setAttribute("SortingColumnTitle", mSortingColumnTitle);
    
    // Store sorting direction state
    stateInfoBody->setAttribute("SortingDirection", mSortingDirection);
    
    stateInfo.addChildElement(stateInfoBody);
    copyXmlToBinary(stateInfo, destData);
}

And in these two methods:

XmlElement SampleLibraryManager::loadFileAsXml(File& inFile)
{
    const InterProcessLock::ScopedLockType scopedLock(fileLock);
    
    MemoryBlock fileData;
    inFile.loadFileAsData(fileData);
    XmlElement fileXml = *AudioPluginInstance::getXmlFromBinary(fileData.getData(), (int) fileData.getSize());
    
    return fileXml;
}
void SampleLibraryManager::writeXmlToFile(XmlElement& inXml, File& inFile)
{
    const InterProcessLock::ScopedLockType scopedLock(fileLock);
    
    MemoryBlock destinationData;
    AudioPluginInstance::copyXmlToBinary(inXml, destinationData);
    inFile.replaceWithData(destinationData.getData(), destinationData.getSize());
}

The code to the whole project can be found here if interested or needed: GitHub

Some help would be greatly appreciated on how to solve this!

Thanks in advance :slight_smile:

The leaked typeface is created when calling setDefaultLookAndFeel in the constructor of
SaemplAudioProcessorEditor. It looks like the font is used by an AlertWindow.

  • Are you creating an alert window in your editor constructor? If so, is this alert window definitely destroyed when the editor is destroyed?
  • How are you constructing your custom look and feel, and is it definitely destroyed before the application exits? Normally, when calling setDefaultLookAndFeel with a custom look and feel, there should be a matching call to setDefaultLookAndFeel (nullptr) on shutdown.
1 Like

Okay, so I forgot about the setDefaultLookAndFeel(nullptr) in the deconstructor but now its in. Leak still happens with the alert window. I use alert windows in two places:

inline void showFileDeletedWarning()
{
    // Show warning popup message
    AlertWindow::showAsync(MessageBoxOptions()
                           .withIconType(MessageBoxIconType::NoIcon)
                           .withTitle("File not available!")
                           .withMessage("This file has probably been externally deleted and was removed from the list of available samples.")
                           .withButton("OK"),
                           nullptr);
}
AlertWindow::showAsync(MessageBoxOptions()
                                   .withIconType(MessageBoxIconType::NoIcon)
                                   .withTitle("Sample Library Chooser")
                                   .withMessage("You picked: " + name)
                                   .withButton("OK"),
                                   nullptr);

could that inline method be an issue? I don’t know how to properly handle these types of helper methods in C++.

I also have another kind of helper class with lots of styling definitions like this:


[...]
static Colour const COLOUR_ACCENT_LIGHT = Colour(207, 191, 163);
static Colour const COLOUR_ACCENT_LIGHT_STRONG_TRANSPARENT = Colour(COLOUR_ACCENT_LIGHT).withAlpha(0.99f);

static Colour const COLOUR_ACCENT_MEDIUM = Colour(71, 24, 19);
static Colour const COLOUR_ACCENT_MEDIUM_STRONG_TRANSPARENT = Colour(COLOUR_ACCENT_MEDIUM).withAlpha(0.99f);

static Colour const COLOUR_ACCENT_DARK = Colour(33, 16, 15);
static Colour const COLOUR_ACCENT_DARK_STRONG_TRANSPARENT = Colour(COLOUR_ACCENT_DARK).withAlpha(0.99f);


// Fonts
static Font const FONT_SMALL("Helvetica Neue", 12.00f, Font::plain);
static Font const FONT_SMALL_BOLD("Helvetica Neue", 12.00f, Font::bold);
static Font const FONT_SMALL_BOLD_ACCENTUATED("Helvetica Neue", 13.00f, Font::bold);

static Font const FONT_MEDIUM_SMALL("Helvetica Neue", 16.00f, Font::plain);
static Font const FONT_MEDIUM_SMALL_BOLD("Helvetica Neue", 16.00f, Font::bold);

static Font const FONT_MEDIUM("Helvetica Neue", 22.00f, Font::plain);
static Font const FONT_MEDIUM_BOLD("Helvetica Neue", 22.00f, Font::bold);

static Font const FONT_LARGE("Helvetica Neue", 48.00f, Font::plain);
static Font const FONT_LARGE_BOLD("Helvetica Neue", 48.00f, Font::bold);

Is this a viable way to handle style definitions like fonts, colours or component width values?

Thanks again

static variables are destroyed after the juce LeakDetector, so it’s most likely those Fonts you declared here.

I would recommend a SharedResourcePointer:

struct MyStyling
{
    using juce;
    using Ptr=SharedResourcePointer<MyStyling>;

    Colour const COLOUR_ACCENT_LIGHT = Colour(207, 191, 163);
    Colour const COLOUR_ACCENT_LIGHT_STRONG_TRANSPARENT = Colour(COLOUR_ACCENT_LIGHT).withAlpha(0.99f);
    // ...

// Fonts
    Font const FONT_SMALL("Helvetica Neue", 12.00f, Font::plain);
    Font const FONT_SMALL_BOLD("Helvetica Neue", 12.00f, Font::bold);
    Font const FONT_SMALL_BOLD_ACCENTUATED("Helvetica Neue", 13.00f, Font::bold);

    Font const FONT_MEDIUM_SMALL("Helvetica Neue", 16.00f, Font::plain);
    Font const FONT_MEDIUM_SMALL_BOLD("Helvetica Neue", 16.00f, Font::bold);

    Font const FONT_MEDIUM("Helvetica Neue", 22.00f, Font::plain);
    Font const FONT_MEDIUM_BOLD("Helvetica Neue", 22.00f, Font::bold);

    Font const FONT_LARGE("Helvetica Neue", 48.00f, Font::plain);
    Font const FONT_LARGE_BOLD("Helvetica Neue", 48.00f, Font::bold);
}

// use it:
MyStyling::Ptr style;

g.setFont (style->FONT_LARGE);

To avoid the object going out of scope, you can add a dummy Ptr to your main or AudioProcessor or AudioProcessorEditor. That way it is only created and destroyed once.

1 Like

Thanks @reuk and @daniel ! This solved my issues. It also made the memoryblock leak disappear, which I don’t get but great anyway :smiley: With the struct MyStyling in the StyleDefinitions.h and the shared resource pointer inside the struct I could use the values within the component classes through including the MyStyling::Ptr style and then always using style->VARIABLE_NAME. I just don’t get how this works exactly. Is there some kind of name for this concept? It was a bit more convenient before because I didn’t have to use the “style->” part before and had to change this in probably over 300 places. Is there any way I could get around the fact that I have to put style-> in front everytime I want to use one of the styling definitions?

To avoid the object going out of scope, you can add a dummy Ptr to your main or AudioProcessor or AudioProcessorEditor. That way it is only created and destroyed once.

What did you mean by this? Is it created multiple times if I add the MyStyling::Ptr style to every component that uses it?

Thanks again for the help, was very useful and I think I learned something new too!

The SharedResourcePointer is similar to a singleton. So it only ever exists once.
A Singleton is created once lazily and remains for the lifetime of the process.

The SharedResourcePointer also exists only once. But it has a reference count, and if no Ptr object exists anywhere, it is destroyed. That means it would create the typefaces every time.
To avoid that you simply add a Ptr instance to your AudioProcessorEditor, which keeps the reference count above zero and makes sure, the MyStyle is kept in memory allowing easy access.
Once the last AudioProcessorEditor goes out of scope, it is cleanly disposed.

1 Like