question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Using a font resource with FontFamily will cause UnmanagedMemoryStream leaks

See original GitHub issue

Background

This is a known WPF issue but still affects applications created with Material Design XAML Toolkit since it relies on the Roboto font. See https://stackoverflow.com/a/31452979. The answer at StackOverflow has a broken link to a issue at Microsoft Connect. I have not been able to find out if this bug is going to be fixed. I have not found any official comments about this issue.

According to my observations the leaked object UnmanagedMemoryStream is very small in size, about 32 bytes. This makes it almost invisible to the memory footprint of many applications. I observed this leak in an application with many bindings which are updated frequently. The application is not expected to be restarted often. These traits would lead to huge amounts of UnmanagedMemoryStream being created over time.

How to reproduce

You need Visual Studio Diagnostics Tool, dotMemory or another memory profiler to observer the memory leak. I have used both Visual Studio Diagnostics Tool and dotMemory to track down the leak and confirm it has to do with font loading.

Visual Studio Diagnostics Tool

  1. Start demo project
  2. Take snapshot 1
  3. Navigate to “Fields” page
  4. Take snapshot 2
  5. Change text field “Name”
  6. Take snapshot 3
  • Snapshot 1 will have about 162 instances of UnmanagedMemoryStream
  • Snapshot 2 will have about 36 new instances of UnmanagedMemoryStream
  • Snapshot 3 will have about 3 new instances of UnmanagedMemoryStream

Workaround

The workaround suggested by Israel Altar is to use file based fonts instead of resource based fonts. I have confirmed that this workaround “works on my machine”.

  1. Create a static FontFamily instance:
public class MaterialDesignFonts
{
    public static FontFamily Roboto { get; }

    static MaterialDesignFonts()
    {
        var fontPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Resources\Roboto\");            
        Roboto = new FontFamily(new Uri($"file:///{fontPath}"), "./#Roboto");            
    }
}
  1. Add the Roboto fonts to the project.

Add the fonts with “Build Action” None and “Copy to Output Directory” Copy if newer

<None Include="Resources\Roboto\Roboto-Black.ttf"> 
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> 
</None> 
<None Include="Resources\Roboto\Roboto-BlackItalic.ttf"> 
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> 
</None> 
<None Include="Resources\Roboto\Roboto-Bold.ttf"> 
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> 
</None> 
<None Include="Resources\Roboto\Roboto-BoldItalic.ttf"> 
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> 
</None> 
<None Include="Resources\Roboto\Roboto-Italic.ttf"> 
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> 
</None> 
<None Include="Resources\Roboto\Roboto-Light.ttf"> 
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> 
</None> 
<None Include="Resources\Roboto\Roboto-LightItalic.ttf"> 
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> 
</None> 
<None Include="Resources\Roboto\Roboto-Medium.ttf"> 
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> 
</None> 
<None Include="Resources\Roboto\Roboto-MediumItalic.ttf"> 
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> 
</None> 
<None Include="Resources\Roboto\Roboto-Regular.ttf"> 
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> 
</None> 
<None Include="Resources\Roboto\Roboto-Thin.ttf"> 
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> 
</None> 
<None Include="Resources\Roboto\Roboto-ThinItalic.ttf"> 
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> 
</None> 
<None Include="Resources\Roboto\RobotoCondensed-Bold.ttf"> 
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> 
</None> 
<None Include="Resources\Roboto\RobotoCondensed-BoldItalic.ttf"> 
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> 
</None> 
<None Include="Resources\Roboto\RobotoCondensed-Italic.ttf"> 
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> 
</None> 
<None Include="Resources\Roboto\RobotoCondensed-Light.ttf"> 
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> 
</None> 
<None Include="Resources\Roboto\RobotoCondensed-LightItalic.ttf"> 
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> 
</None> 
<None Include="Resources\Roboto\RobotoCondensed-Regular.ttf"> 
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> 
</None>  
  1. Change the FontFamily attributes on your Window types. This is the only place I use the Roboto font in my application:
FontFamily="{x:Static local:MaterialDesignFonts.Roboto}"
TextElement.FontFamily="{x:Static local:MaterialDesignFonts.Roboto}"

Future

How do we handle this issue in Material Design XAML Toolkit? Possible actions below but I let you guys weigh in on the way forward.

  • Write a wiki page about font usage and describe the workaround for those in need of it
  • Let the library solve this issue:
    • Keep the font as a resource in the library
    • Add a MaterialDesignFonts type that takes care of writing temporary font files to disk before loading
    • The consumer of the library can decide if they want to use {StaticResource MaterialDesignFont} or {x:Static materialDesign:MaterialDesignFonts.Roboto}

Personally I think it would be nice if I could use the material design fonts without memory leakage out of the box 😃

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Reactions:9
  • Comments:8 (7 by maintainers)

github_iconTop GitHub Comments

3reactions
quicolicommented, Dec 6, 2019

Have you seen this different solution: using a markup extension to fix memory leak

With this:

  • the font can be kept in resources (no breaking changes here)
  • a markup extension is created (no breaking changes here)
  • The way to set the font in a Window would change (breaking change)

FontFamily="{local:FontExplorer Key=Roboto}">

2reactions
Keboocommented, Dec 8, 2019

@quicoli I have not seen that. I think I like it better, though I think i am going to leave in the option for people to include the font files in their project if they want. I dropped the MaterialDesignFonts static class in favor of the MaterialDesignFont markup extension.

Read more comments on GitHub >

github_iconTop Results From Across the Web

WPF TextBlock memory leak when using Font
A FontFamily leaks UnmanagedMemoryStreams when it is used if it was sourced from an embedded resource or a relative path.
Read more >
Packaging Fonts with Applications - WPF .NET Framework
You can easily package fonts as resources within your WPF applications to display user interface text and other types of text based content....
Read more >
Add a font as an XML resource
In Android, you can create a new font family as an XML resource and access it as a single unit, instead of referencing...
Read more >
Font resource | Substance 3D Designer
Fonts are always embedded in an SBSAR, no matter if from a linked Resource, or by using a system-installed font. The advantage of...
Read more >
Using Fonts — Godot Engine (stable) documentation in English
Godot allows you to set specific fonts for different UI nodes. There are three different ... However, this can cause downscaled fonts to...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found