Typeface::createSystemTypefaceFor leaking on WIndows

Hi, folks.
The docs for createSystemTypefaceFor say:

/** Attempts to create a font from some raw font file data (e.g. a TTF or OTF file image).
The system will take its own internal copy of the data, so you can free the block once
this method has returned.

The typeface will remain registered with the system for as long as there is at least one
owner of the returned Ptr. This allows typefaces registered with createSystemTypefaceFor to
be created using just a typeface family name, e.g. in font fallback lists.
*/

However, on Windows, typefaces allocated this way may be leaking. If I give my plugin editor a Font member and initialize it using FontOptions with a typeface returned from this method, I think the typeface might not be properly cleaned up when the editor is closed its Font member is destroyed.
Here’s a minimal plugin example which has this problem. It’s essentially the audio plugin example, but it loads a font from BinaryData.

Loading this in Reaper and repeatedly opening and closing its editor, Visual Studio’s heap snapshots, taken while the editor is closed, show typeface-related objects accumulating on the heap:

I’m testing with the current tip of the develop branch (e26c69e7bf35c9c800ac826c74d258d5fbafd1eb).

Thanks for taking a look!

I don’t think this is actually leaking. JUCE internally stores some caches related to shaped text, including fonts. In the example you provided, the AudioProcessorEditor creates a new Typeface each time the editor is opened, and this typeface is then used to shape some text before drawing it on-screen. Text shaping can be a fairly heavy operation, so the shaping result is cached for re-use. However, because the typeface is rebuilt from scratch each time the editor is opened, there will be a cache miss when shaping the text after reopening the editor, and the text will be reshaped and added to the cache. I’d expect you to see that the memory usage stabilises at a certain point, once the cache fills up and entries start to be removed as well as added.

You might see slightly better performance if you store the custom Typeface in a long-lived location (e.g. in your AudioProcessor) and reference that typeface in your Editor. This way, you’ll avoid reloading the typeface each time the editor is opened, and you’ll also get more cache hits when shaping text that doesn’t change between Editor instances.

1 Like

Thanks for taking a look so soon.

Keeping the font stored in the processor is an interesting tip. I’ll definitely pass that along to my team.

Just in the interest of determining if there is actually a leak, do you have an estimate for what we could expect the bounds of that cache to look like? I seem to be able to get the memory usage to creep up indefinitely by opening and closing the editor. Here’s the memory profiler showing my memory usage (plus Reaper’s) doubling, just from opening and closing the editor.

I’m afraid not, this will depend on the font that is used, the text that is shaped, the quantity of different strings in your interface, and potentially other factors too. The cache’s maximum size is specified as a number of shaped strings to store, so it might be possible to construct a pathological case with lots of very long strings and heavy fonts. However, I wouldn’t expect memory-related issues under standard usage.

If you’re trying to detect leaks, you might get better results by using JUCE’s built-in tools, such as the JUCE_LEAK_DETECTOR macro, or a dedicated leak-checker tool like LeakSanitizer. Generally, you can’t be sure that an object has been leaked until the program has completely exited, so checking the heap usage while the program is running is unlikely to help you find leaks. It may help you to track down the cause of unreasonably high memory usage at a particular point in the program, though.

Hey @reuk, I’m working on this problem with @mardiqwop.

Off the bat, I think by using the term ā€œleakā€ we are not properly conveying the problem at hand. The memory is eventually cleaned up when the plugin is destructed. The problem at hand is that every time we open/close the editor, more memory is allocated, and it remains allocated until the whole plugin is destructed. I’ve seen this called a space leak, but memory bloat is probably the clearer term here.

We have been calling createSystemTypefaceFor() in our editor constructors for over a decade, without issue. Here is a screenshot where I’m running @mardiqwop’s example on the JUCE 7.0.12 release, opening / closing the editor 20 times in between each snapshot. The memory usage is essentially flat. (Obviously Reaper is also doing it’s own memory management, but I’ve tested this several times and it seems consistent. I’ve also tested multiple DAWs):

Building with JUCE 8 develop and doing the same test, the memory grows substantially:

This test might seem excessive over normal ā€œuse caseā€, but in a session with dozens (hundreds?) of plugins run for hours, or say in an embedded live-use DAW where the session is sometimes left running for days, it could add up. Not to mention this is a tiny plugin with one font and string, whereas our actual plugins have multiple fonts and tons of strings. In a production Eventide plugin, I’m seeing 2-3MB being created and persisting for each editor open/close.

We can update how we call createSystemTypefaceFor() and cache the typefaces with the processor, but it’s an annoying and painful change to do for our entire plugin line. And of course, this will affect all JUCE plugins depending on how and when they call createSystemTypefaceFor(). I would guess this doesn’t affect just us.

Lastly - what is the current recommended method for creating an embedded font to use in a JUCE plugin? We don’t care about fallback fonts or anything else, we just want a juce::Font that uses my embedded .ttf, and that doesn’t keep adding memory every time our editor is created. The docs for createSystemTypefaceFor() say The typeface will remain registered with the system for as long as there is at least one owner of the returned Ptr, which implies that once out editor is closed, the Typeface will get deallocated since the Font owning the pointer will get destructed. Perhaps those need to get updated as well?

1 Like

I should also mention, this doesn’t seem to occur on macOS. Memory usage seems stable there.

Thanks, I took another look and found that we’re allocating a copy of the loaded font data each time createSystemTypefaceFor is called. These allocations weren’t bounded by the size of the glyph arrangement cache, so they could continue accumulating each time the editor was opened. This is unnecessary, so we’ve pushed a change that reuses an existing buffer when possible:

Please could you try this change in your plugin(s) and check whether it improves the memory usage at all?

The glyph arrangement caches are still in place, so they may still hold onto Typeface instances that are no longer referenced anywhere outside JUCE library code. I expect this to be less of an issue, seeing as the cache size is bounded, but let me know if it’s actually problematic for your use-case. Then, we can think about ways to limit memory usage there, too.

what is the current recommended method for creating an embedded font to use in a JUCE plugin?

I think your original approach (creating the Typeface, ideally just once, in the AudioProcessorEditor and then referencing that instance throughout your UI) should be fine in most scenarios. You could reduce memory usage a bit more by using a SharedResourcePointer or similar to hold ā€˜global’ resources like fonts, and keep an instance of the SharedResourcePointer in the AudioProcessor. This way, you’ll only ever have one copy of the typeface loaded, even if you have multiple plugin editors open. You’ll also only need to load the face when the plugin is initially instantiated, instead of each time the editor is opened.

I believe the docs are still accurate, but the subtlety is that the Typeface::Ptr may also be owned by library internals. i.e. ā€œownerā€ refers to all owners of the pointer, not just owners in user code. If any of those owners outlive the editor, then the typeface will outlive the editor too.

I hope that helps; please let us know if you have any further questions, or if you’re still seeing problems with accumulating memory usage.

2 Likes

Hey @reuk, thanks for the quick hotfix! Yes, we can confirm that this change improves the memory usage. The memory does not continue to grow as it did before when opening/closing the UI, and quickly stabilizes. This is sufficient for us.

I think your original approach (creating the Typeface, ideally just once, in the AudioProcessorEditor and then referencing that instance throughout your UI) should be fine in most scenarios.

Great, we’ll stick with this. We’re comfortable with the memory usage being slightly larger than if we were using a SharedResourcePointer - it was the continued growth that was alarming.

I believe the docs are still accurate, but the subtlety is that the Typeface::Ptr may also be owned by library internals. i.e. ā€œownerā€ refers to all owners of the pointer, not just owners in user code. If any of those owners outlive the editor, then the typeface will outlive the editor too.

Even if the docs are accurate, it would be still helpful if they called out the subtlety you mention. That way, we’d know at a glance that the Typeface might not be destroyed when the returned Ptr is destroyed. It’s probably less of an issue with the cache now in place, but nonetheless helpful for knowing what to expect from the function.

Thanks!