views:

491

answers:

5

I'm aware that the best practice is to call Dispose on any object that implements IDisposable, especially objects that wrap finite resources like file handles, sockets, GDI handles, etc.

But I'm running into a case where I have an object that has a Font, and I would have to plumb IDisposable through several layers of objects, and review a lot of usages, to make sure I always get the Font disposed. And I'm wondering whether it's worth the complexity.

It would be one thing if Font wrapped an HFONT, because GDI resources are system-global. But Font doesn't wrap a GDI handle; it's GDI+, which is a completely separate system, and as far as I understand, is process-local, not system-global like GDI. And unlike Image, Font doesn't ever hold onto filesystem resources (that I know of, anyway).

So my question is: What is the real cost of letting a Font get garbage collected?

I know I would take a small hit for the finalizer, but if the number of "leaked" Fonts is small (say half a dozen), that hit honestly wouldn't be noticeable. Apart from the finalizer, this doesn't seem much different from allocating a mid-sized array and letting the GC clean it up -- it's just memory.

Are there costs I'm not aware of in letting a Font get GCed?

+2  A: 

The problem is that garbage collection only happens when there is memory pressure. Often, unmanaged handles are more restricted than memory, so you can run out of handles before GC happens, causing errors.

But for one or two Font instances, it won't hurt you overly.

A bigger problem is that some of the objects are shared and shouldn't (or can't ) be disposed prematurely...

Marc Gravell
"Often, unmanaged handles are more restricted than memory" -- Sure. But is that true for GDI+ font handles specifically? That's part of my question.
Joe White
+2  A: 

Simple answer: if its just a few, then no. If it's a lot, then yes. If your application is already stressing the garbage collector, then yes. I would use perfmon to view the number of objects sitting around, and the number getting promoted to higher generations, and then decide.

David Hill
+2  A: 

Why wouldn't you dispose of it once you're done? Just because we have street sweepers doesn't mean we should just go around littering the streets. However, in the example given, if the font is needed for the lifetime of the object then dispose of the font in that object's dispose. There are many things that would simplify my code too, but that doesn't justify those changes - sometimes there are things that you should do, even though it's a pain.

It's always a good idea to tidy up after yourself. When you no longer need something, dispose of it. That way you can avoid nasty race conditions, out of memory exceptions, drawing glitches, and lengthy, processor-intensive garbage collections.

I find that it's best practice to dispose of disposable objects if you no longer need them unless there is a justifiable reason not to (such as you don't own the object). It is more difficult to track down the root cause of a problem than it is to code defensively up front.

With regards to Font, MSDN says:

Always call Dispose before you release your last reference to the Font. Otherwise, the resources it is using will not be freed until the garbage collector calls the Font object's Finalize method.

It doesn't say what the resources are but the fact that they explicitly state that this should be done implicitly adds importance to calling Dispose.

Jeff Yates
Why wouldn't you dispose of it? Well the OP did say why: because it would tremendously simplify his code.
+1  A: 

Finalizers are built into classes specifically because the cleanup is necessary. Regardless of whether you have a large or small amount of objects to clean up, it's good practice to clean them up.

The GC was built to have a pseudo-mind of its own. By properly disposing of your objects, you're allowing the GC to do what it was made to do.

However, if you're creating a large number of font objects and disposing of all of them, it may be beneficial to call the GC on the appropriate generation (probably generation 0) every so often to initiate a GC cleanup yourself depending on what kinds of other objects you're instantiating in great number. Your goal should be to keep objects you know you're not using for very long from getting promoted to elder generations. This keeps the GC's job lean & mean.

Just use your best judgement and you'll be fine. But do indeed dispose of any object with a finalizer as your normal practice.

Boydski
A: 

How important is disposing of anything, really? IMHO when you start asking these kind of questions, it sounds like you're having a design problem in your code. You should always dispose of things you don't need anymore - that's called responsible programming.

Possible solutions to your problem:

  • Do not pass around objects like Fonts. Implement the font-using logic in one place (one class), add the Font as a field of that class and implement IDisposable for that class.

  • Implement a Font cache class - instead of creating new Font objects using the new operator all over your code, use this class to get the desired Font. The class can then have the logic to reuse existing Fonts, if possible. Or to keep last 10 fonts in memory, and dispose of the others. Implement IDisposable for the cache, which will be called once in your app's lifecycle.

Igor Brejc