.NET FontFamily leak error in Windows 10

On Windows 10, the System.Drawing.FontFamily.IsStyleAvailable method seems to leave the allocated space in memory even after calling the Dispose method.

I wrote a simple console application to test it:

using System;
using System.Drawing;
using System.Diagnostics;

namespace ConsoleApplication1
{
    class Program
    {
        static string getMemoryStatusString()
        {
            using (Process p = Process.GetCurrentProcess())
            {
                return "(p: " + p.PrivateMemorySize64 + ", v:" + p.VirtualMemorySize64 + ")";
            }
        }

        static void Main(string[] args)
        {
            string s = getMemoryStatusString();
            foreach(FontFamily fontFamily in FontFamily.Families)
            {
                Console.Write(fontFamily.Name + " " + getMemoryStatusString() + " -> ");

                fontFamily.IsStyleAvailable(FontStyle.Regular);
                fontFamily.Dispose();

                Console.WriteLine(getMemoryStatusString());
            }
            string e = getMemoryStatusString();
            Console.WriteLine(s + " -> " + e);
            Console.ReadLine();
        }
    }
}

Any idea on why this is happening?

Thanks in advance!

+4
source share
3 answers

If there is a memory leak, it will be in gdiplus.dll, FontFamily.IsStyleAvailable()actually making an external call GdipIsStyleAvailable().

From ILSpy:

public bool IsStyleAvailable(FontStyle style)
{
    int num2;
    int num = SafeNativeMethods.Gdip.GdipIsStyleAvailable(new HandleRef(this, this.NativeFamily), style, out num2);
    if (num != 0)
    {
        throw SafeNativeMethods.Gdip.StatusException(num);
    }
    return num2 != 0;
}

This, in turn, is defined as:

[DllImport("gdiplus.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
internal static extern int GdipIsStyleAvailable(HandleRef family, FontStyle style, out int isStyleAvailable);
0
source

( Windows 7 .Net 4.5, ). .

static string getMemoryStatusString() {
  // Do not forget to collect the garbage (all the generations)
  GC.Collect(2);
  GC.WaitForFullGCComplete(); 

  using (Process p = Process.GetCurrentProcess()) {
    return "(p: " + p.PrivateMemorySize64 + ", v:" + p.VirtualMemorySize64 + ")";
  }
}

// Suspected method
static void methodUnderTest() {
  foreach (FontFamily fontFamily in FontFamily.Families) {
    //Console.Write(fontFamily.Name + " " + getMemoryStatusString() + " -> ");
    fontFamily.IsStyleAvailable(FontStyle.Regular);
    //TODO: You must not do this: disposing instanse you don't own  
    fontFamily.Dispose(); 
  }
}

// Test itself
static void Main(string[] args) {
  // Warming up: let all the libraries (dll) be loaded, 
  // caches fed, prefetch (if any) done etc.
  for (int i = 0; i < 10; ++i) 
    methodUnderTest();

  // Now, let run the test: just one execution more
  // if you have a leak, s1 and s2 will be different
  // since each run leads to leak of number of bytes
  string s1 = getMemoryStatusString();

  methodUnderTest();  

  string s2 = getMemoryStatusString();

  Console.Write(s1 + " -> " + s2);
}

, :

  (p: 59453440, v:662425600) -> (p: 59453440, v:662425600)
0

. , GC.Collect. , GC.Collect .

, , , .

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
0

Source: https://habr.com/ru/post/1613663/


All Articles