Skip to content

Using embedded font resources leaks memory #7289

Closed
@kitgrose

Description

@kitgrose
  • .NET Core Version: 7.0
  • Windows version: Windows 10 version 21H2 (OS Build 19044.2251)
  • Does the bug reproduce also in WPF for .NET Framework 4.8?: Yes
  • Is this bug related specifically to tooling in Visual Studio (e.g. XAML Designer, Code editing, etc...)? No

Problem description:
When a custom font is loaded as a Resource and used in the application, the application slowly leaks memory via an UnmanagedMemoryStream object that grows apparently without bounds, regardless of GC collection.

The issue does not appear to affect fonts with Build Action set to Content (although publishing fonts this way may be against their respective EULAs).

Actual behavior:
When a text block is rapidly changed between a series of embedded fonts, Visual Studio's Diagnostic Tools shows a UnmanagedMemoryStream object growing by approximately 35–40 KB per second.

Expected behavior:
Once all the custom fonts are loaded, changing the FontFamily property of a TextBlock shouldn't increase memory usage at all.

Minimal repro:

  1. Create a new WPF project (the issue can be reproduced in .NET 7.0 and .NET Framework v4.8 at least, but I can find references to it online from earlier referencing now-unavailable MS Connect posts).
  2. Add some typefaces to the project (I've got a combination of OTF and TTF fonts in my test app), and mark their Build Action as Resource.
  3. Add those fonts to your application resources in App.xaml (exact code will differ based on the fonts you choose):
    <Application.Resources>
      <FontFamily x:Key="Flama">./Fonts/#Flama</FontFamily>
      <FontFamily x:Key="FlamaMedium">./Fonts/#Flama Medium</FontFamily>
      <FontFamily x:Key="IntroCaps">./Fonts/#Intro Caps</FontFamily>
      <FontFamily x:Key="IntroBlackInline">./Fonts/#Intro Inline Caps</FontFamily>
      <FontFamily x:Key="Pacifico">./Fonts/#Pacifico</FontFamily>
    </Application.Resources>
  4. In MainWindow.xaml, create a TextBlock:
    <TextBlock x:Name="TestBlock" FontSize="48">Test block</TextBlock>
  5. Add a new Loaded event handler for MainWindow that cycles through your embedded fonts to use as the text block's font family as fast as possible:
    private void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        var resourceEnumerator = Application.Current.Resources.GetEnumerator();
        var fontChanger = new DispatcherTimer();
        fontChanger.Tick += (s, e) =>
        {
            if (!resourceEnumerator.MoveNext())
            {
                resourceEnumerator.Reset();
                resourceEnumerator.MoveNext();
            }
    
            if (resourceEnumerator.Value is FontFamily fontFamily)
            {
                TestBlock.FontFamily = fontFamily;
            }
        };
        fontChanger.Start();
    }
  6. Run the app and observe the memory usage steadily increasing over time. Diagnostic tools will show the largest culprit for the growth being the UnmanagedMemoryStream:
    wpf-font-memory-leak
  7. Change the font resources to have a Build Action of Content, and set the Copy to Output Directory property to Copy if newer, and re-run the test.
  8. Observe that now the memory usage does not appear to grow over time.

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions