BUG in System.Drawing.Font!

B

Barry Anderberg

I'm using the .NET Memory Profiler by Sci Tech and I wrote a little
test application to verify something odd I observed and it appears
that System.Drawing.Font fails to dispose of its FontFamily.

I run the following code:

// BEGIN
while( true )
{
System.Drawing.Graphics dc=lblClock.CreateGraphics();
dc.Clear(Color.White);
Font f=new Font("Arial", 10, FontStyle.Bold);
dc.DrawString(DateTime.Now.ToString("yyyy MMM dd, hh:mm:ss",
ClockDTFI), f, Brushes.Black, 5, 0);
f.Dispose();
dc.Dispose();
}
// END

This is just to see if I can generate lots of fonts, dispose of them,
and verify that they are disposing of their unmanaged resources, but
the .NET Memory Profiler shows that there are *thousands* of
FontFamily objects that haven't been disposed.

I am not certain but I suspect this is causing a memory leak because
my program's memory grows and grows.

Even if I call f.FontFamily.Dispose() it still says there are
thousands of FontFamily objects that haven't been disposed.
 
C

Chris R. Timmons

(e-mail address removed) (Barry Anderberg) wrote in
I'm using the .NET Memory Profiler by Sci Tech and I wrote a
little test application to verify something odd I observed and
it appears that System.Drawing.Font fails to dispose of its
FontFamily.

I run the following code:

// BEGIN
while( true )
{
System.Drawing.Graphics dc=lblClock.CreateGraphics();
dc.Clear(Color.White);
Font f=new Font("Arial", 10, FontStyle.Bold);
dc.DrawString(DateTime.Now.ToString("yyyy MMM dd, hh:mm:ss",
ClockDTFI), f, Brushes.Black, 5, 0);
f.Dispose();
dc.Dispose();
}
// END

This is just to see if I can generate lots of fonts, dispose of
them, and verify that they are disposing of their unmanaged
resources, but the .NET Memory Profiler shows that there are
*thousands* of FontFamily objects that haven't been disposed.

I am not certain but I suspect this is causing a memory leak
because my program's memory grows and grows.

Even if I call f.FontFamily.Dispose() it still says there are
thousands of FontFamily objects that haven't been disposed.

Barry,

It's not a memory leak. Unmanaged resources and memory are two
different things. The calls to IDisposable.Dispose are properly
freeing the unmanaged resources (i.e. the low level font handles).
Otherwise, you would be getting some kind of exception about no
available handles.

The memory used by the Graphics and Font instances is managed by the
Garbage Collector (GC). It simply hasn't gotten around to reclaiming
the memory used by those objects. If you're an experienced C, C++ or
Delphi developer, it may be hard to ignore the growing memory usage,
but that's usually the best thing to do. The GC is a very well
written piece of software. Trust it to do its job.

MSDN has several good articles explaining how the GC works:


http://msdn.microsoft.com/msdnmag/issues/1100/GCI/TOC.ASP

http://msdn.microsoft.com/msdnmag/issues/1200/GCI2/TOC.ASP

http://msdn.microsoft.com/library/en-
us/dndotnet/html/dotnetgcbasics.asp

(TinyURL: http://tinyurl.com/2tzpn)

http://msdn.microsoft.com/library/en-
us/dndotnet/html/dotnetperftechs.asp

(TinyURL: http://tinyurl.com/3890)


Hope this helps.

Chris.
 
B

Barry Anderberg

Chris,

I am not sure I agree with you. I am familiar with the GC and how/when
it collects garbage. It collects once the managed heap is full, and at
some other times. So I wrote the test application to deliberately fill
the heap to force a garbage collection. I also added a button to the
form to call GC.Collect() and according to the memory profiler, these
objects have not been disposed. I realize that they have been garbage
collected, but that does not mean there are no leaks. If unmanaged
resources are not freed, the garbage collector will not do it for you.
The unmanaged resources are freed by the underlying framework code when
the object is disposed, or in the finalize method of the class in
question. The only thing I can assume is that the finalize method is
doing the job, but I still think it's a bug in the framework that the
Dispose method of the Font's objects aren't being called. Any class
that implements IDisposable should have its dispose method called. The
finalize method is a last resort when the programmer does not call
Dispose like he should. Isn't that your understanding as well?

Again, you're probably right that there's no leak but it makes for
considerable confusion when running a memory profiler that tracks
undisposed objects. We have a major piece of software at my job and it
is slowly eating up the memory and eating up the entire pagefile causing
major fragmentation of the pagefile, ultimately slowing down the system
and causing other remoted applications to begin timing out. I've been
spending lots of time pouring over code and running this memory profiler
and I'm at a loss as to why this is happening. The FontFamily class is
only one of many objects that the .NET framework is not disposing of
even though the classes implement IDisposable. That's simply bad
programming on the part of Microsoft. One thing to note is that we're
using the 1.0 Framework which may explain these issues. Perhaps the
objects are being properly disposed of in the newer .NET Framework.

Thanks for the reply,

Barry
 
C

Chris R. Timmons

Chris,

I am not sure I agree with you. I am familiar with the GC and
how/when it collects garbage. It collects once the managed heap
is full, and at some other times. So I wrote the test
application to deliberately fill the heap to force a garbage
collection. I also added a button to the form to call
GC.Collect() and according to the memory profiler, these objects
have not been disposed. I realize that they have been garbage
collected, but that does not mean there are no leaks. If
unmanaged resources are not freed, the garbage collector will
not do it for you. The unmanaged resources are freed by the
underlying framework code when the object is disposed, or in the
finalize method of the class in question. The only thing I can
assume is that the finalize method is doing the job, but I still
think it's a bug in the framework that the Dispose method of the
Font's objects aren't being called. Any class that implements
IDisposable should have its dispose method called. The finalize
method is a last resort when the programmer does not call
Dispose like he should. Isn't that your understanding as well?

Again, you're probably right that there's no leak but it makes
for considerable confusion when running a memory profiler that
tracks undisposed objects. We have a major piece of software at
my job and it is slowly eating up the memory and eating up the
entire pagefile causing major fragmentation of the pagefile,
ultimately slowing down the system and causing other remoted
applications to begin timing out. I've been spending lots of
time pouring over code and running this memory profiler and I'm
at a loss as to why this is happening. The FontFamily class is
only one of many objects that the .NET framework is not
disposing of even though the classes implement IDisposable.
That's simply bad programming on the part of Microsoft. One
thing to note is that we're using the 1.0 Framework which may
explain these issues. Perhaps the objects are being properly
disposed of in the newer .NET Framework.

Barry,

I think I duplicated your bug in .Net 1.1.

Depending upon the Font constructor used, each instance of the Font
class creates one or two instances of the FontFamily class for its
own internal use, but never disposes of them.

This code results in two FontFamily instances being created (and not
disposed of):

Font f = new Font("Arial", 10, FontStyle.Bold);
...
f.Dispose();

Whereas passing an existing instance of FontFamily to the Font
constructor only results in one undisposed instance of FontFamily:

FontFamily fontFamily = new FontFamily("Arial");
Font f = new Font(fontFamily, 10, FontStyle.Bold);
...
f.Dispose();
fontFamily.Dispose();

If you want to dig into the .Net framework source code, there is a
great utility called Reflector available at:

http://www.aisto.com/roeder/dotnet/

With it, you can view the decompiled C# source of all of the methods
of the Font and FontFamily classes (and all the rest of the .Net
framework.)

At this point, about the only constructive advice I can offer is the
obvious: minimize the number of instances that don't get properly
disposed. Something like creating a handful of global Font instances
and re-using them throughout the app.

FWIW, here's a short program that demonstrates the problem:

// Compile with "csc /t:exe example.cs"

using System;
using System.Drawing;
using System.Windows.Forms;

namespace TestNamespace
{
public class TestClass
{
[STAThread]
public static void Main()
{
using (Label lblClock = new Label())
{
using (Graphics dc = lblClock.CreateGraphics())
{

// The Font instances get disposed of OK, but
// there will be 2000 undisposed instances of
// FontFamily.

for (int i = 0; i < 1000; i++)
{
dc.Clear(Color.White);
using (Font f = new Font("Arial", 10, FontStyle.Bold))
{
dc.DrawString(DateTime.Now.ToString(
"yyyy MMM dd, hh:mm:ss"), f, Brushes.Black, 5, 0);
}
}
}
}

Console.WriteLine("Done.");
Console.ReadLine();
}
}
}


Chris.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top