Home > Articles > Programming > .NET and Windows Programming

Automatic Memory Management and Unmanaged Resources in .NET

Jim Mischel
  • PrintPrint
  • Share ThisShare This
  • DiscussDiscuss
Close WindowJim Mischel

Jim Mischel

Learn more…

"Highly unlikely" does not mean "impossible"
By on July 18, 2009 No Comments

One of my programs crashed the other day in a very unexpected place.  A call to System.Threading.ConcurrentQueue.TryDequeue (from the Parallel Extensions to .NET) resulted in an OverflowException being thrown.  Investigation revealed a pretty serious bug in the System.Random constructor.

Don't assume too much
By on August 10, 2008 No Comments

We were discussing the design of a collection class at the office the other day, when the topic of parameter checking came up. This particular collection class has a Sort method that works just like the Sort method implemented by the .NET List generic collection. The programmer implementing the collection wanted the constructor to do some checking so that it could set a flag indicating whether the Sort method would actually work. This turns out to be a very bad idea.

What's next for C# and .NET?
By on July 21, 2008 No Comments

About 10 days ago, MSDN's Channel 9 site released an hour-long video entitled Meet the Design Team, that talks in very vague terms about uncoming features in C# 4.0.  You'll learn that the language will include more dynamic constructs and built-in support for multiple cores.  Honestly, that's about all you'll learn from watching the video.  Granted, either one of those broad features implies many changes to the language and to the underlying runtime.

Is this really asynchronous?
By on June 16, 20082 Comments

I've been working on a relatively simple program whose purpose is to see just how fast I can issue Web requests. The idea is to get one machine hooked directly to an Internet connection and see how many concurrent connections it can maintain and how much bandwidth it can consume. A straight bandwidth test is easy: just start three or four Linux distribution downloads from different sites. That'll usually max out a cable modem connection.

Why is this regular expression so slow?
By on June 4, 2008 No Comments

One of the benefits--or curses, depending on my mood and how urgently I need a solution--of programming computers is that I often start working on one thing and end up getting sidetracked by a piece of the problem.  Today's distraction is a regular expression that's a lot slower than I thought it should be.

It's obvious when you think about it
By on May 21, 2008 No Comments

So many bugs that we encounter in our code are obvious in retrospect.  That makes it even worse when we discover them, especially if it takes us a long time to track them down.

Pre-allocating a HashSet
By on May 13, 2008 No Comments
In my most recent .NET Guide update, I mention that it's possible to create a large HashSet by creating a List or other collection, and then passing that collection to the HashSet constructor. However, I did not have a way to create an empty HashSet larger than about 48 million items. A little experimentation after I submitted the column revealed a solution.
Goofy API functions
By on April 16, 2008 No Comments
Sometimes I wonder if API designers think about how people will view their functions.  A good example is the Console.ReadKey method in the .NET Runtime.  
Counting on side effects
By on March 12, 2008 1 Comment

I've long been suspicious of code that depends on side effects, like depending on the order of expression evaluation.  In the past, one had to worry about stuff like this because language standards often didn't specify expression evaluation order, which meant that code that worked when compiled with Microsoft C, for example, wouldn't work when compiled with Borland C (or vice-versa).  I wrote some code like that today in C#, and wondered if it's considered bad practice.

Why no IParseable interface?
By on February 26, 2008 1 Comment

I found it curious that in the .NET Framework objects can implement the IFormattable interface to expose custom formatting, but there is no corresponding IParseable interface. Not only is this curious, it's somewhat frustrating. I was going to write a rant asking why the lack of IParseable and present a possible solution, and then I discovered the very good reason why such a thing hasn't been done.

Take back the desktop!
By on February 19, 2008 1 Comment

At some point in the 30 years or so that I've been working with computers, we've lost sight of the most important fact:  computers are supposed to be tools that serve us.  All too often these days, I feel like I'm the one serving the computer.  At other times, the computer reminds me of the over-eager employee who comes running to the office after completing every minor task, enthusiastically telling me how impressed I should be that he managed to find and actually work the photocopier.

LINQ: I'm a believer
By on February 7, 2008 No Comments

Language-integrated query (LINQ) is a new technology introduced with .NET 3.5.  The C# 3.0 language has been extended to support it.  LINQ is, in essence, a query language for in-memory data.  Think of SQL for your data structures and you get the idea.

Sometimes you have to see a thing in action before you understand just how powerful it is.  The other day I had the opportunity to learn that about LINQ.

Watch those sizes!
By on December 28, 2007 No Comments
I've just spent several frustrating hours debugging some code that uses platform invoke to call Windows API functions.  The frustration was the result of some bad conversions of unmanaged structure types that were defined in the SDK header files.
Can I really ingore that warning?
By on December 6, 2007 No Comments

The .NET Assembly Linker issues warning messages about mismatched assemblies when you build 64-bit .NET applications.  Although the documentation says that you can safely ignore the warnings, the fact that the linker issues them at all is a bug.

The hanging web request
By on October 2, 2007 No Comments
I've had this nagging bug in my program for a while.  Periodically a Web request would just hang for no apparent reason.  I just put it up with it while I had more important fish to fry, but this problem finally worked its way to the top of the priority list.
New IDisposable Article
By on August 7, 2007 No Comments
The .NET IDisposable interface, as simple as it seems, continues to be a source of confusion for new and experienced developers alike.  Shawn Farkas sheds a little more light on IDisposable in the CLR Inside Out article of the July 2007 MSDN Magazine.
Is your program suffering "senior moments"? Jim Mischel shows you how automatic memory management and garbage collection in the .NET Framework can stop those memory leaks.

Automatic memory management, also known as garbage collection, is a much-touted feature of the .NET runtime. With garbage collection, the .NET runtime keeps track of the memory that a program allocates, and can release that memory when the program no longer needs it. The way the .NET garbage collector manages this bit of magic makes for some interesting reading.

TIP

If you're at all curious about how it works, a good place to find more information is Jeffrey Richter's two-part series of articles in the November and December 2000 issues of MSDN Magazine.

The biggest benefit to memory management, as far as a programmer is concerned, is that programmers no longer need to worry about releasing allocated memory. This is unquestionably a Good Thing because memory leaks are among the most common programming errors, and a leading cause of poor program performance and program crashes.

But memory is only one of many scarce resources, and therein lies the problem. Memory leaks are a subset of a more general type of programming error—resource leaks—that share the same cause: the programmer's failure to release an allocated resource. The garbage collector takes care of releasing memory, which is the most commonly forgotten resource, and will even call your object's finalizer before the memory is released, but the finalizer also has to release any file handles, database connections, window handles, or other scarce system resources that it allocates.

Relying on the finalizer to clean up your unmanaged resources, though, is a recipe for disaster. You know that the garbage collector will call your finalizer during the next garbage collection pass, but you don't know when that will happen.

When Is Memory Released?

When you're writing C++ code for Windows, object destructors are executed when you call dispose to deallocate the object. After the object's destructor completes, the memory that the object occupied is returned to the heap. The compiler generates code to call the destructors for stack-allocated objects when the objects go out of scope. This behavior is termed deterministic finalization—object destructors are called at well-defined points in the program.

.NET has no analog to the C++ dispose operation. Once you allocate an object, it remains allocated until the garbage collector, on the next garbage collection pass, determines that the object is no longer being used. At that point, the garbage collector calls the object's finalizer (analogous to a C++ destructor) and then releases the object's memory back to the heap. This is called nondeterministic finalization—you can't say when an object's finalizer will be called. The result is that any memory or unmanaged resources that your object uses remain allocated for an indeterminate time after the object itself is no longer actually in use by your program.

The garbage collector runs on its own schedule. As far as memory is concerned, this doesn't normally cause a problem because the runtime will force a garbage collection if it's unable to satisfy a memory allocation request. But a program that uses many files or makes many different database connections could find itself running out of resources because all of the file handles or connections are allocated by objects that are waiting for finalization. It's kind of like going to Blockbuster Video just after noon and seeing the hot new release behind the counter, but being unable to rent it because it hasn't been officially checked in.

A Simple Example

AllocExample, shown in Listing 1, illustrates this problem. This program performs a loop that allocates a CoolThing, does some processing with it, and then moves on to the next one. CoolThing is a fictitious scarce resource—there are only three of them in the system. Its implementation is shown in Listing 2.

Listing 1 AllocExample.cs

using System;

namespace AllocExample
{
  class Class1
  {
    [STAThread]
    static void Main(string[] args)
    {
      try
      {
        for (int i = 0; i <= 3; i++)
        {
          // allocate a Thing and use it
          CoolThing ct = new CoolThing(i);
          // do stuff with the Thing
          Console.WriteLine("Hello from Thing {0}", ct.ThingNumber);
          // Thing goes out of scope here and can be released
        }
      }
      catch (Exception e)
      {
        Console.WriteLine("Exception: {0}", e);
      }
      Console.WriteLine("Press Enter to exit program");
      Console.ReadLine();
    }
  }
}

Listing 2 CoolThing.cs

using System;

namespace AllocExample
{
  public class CoolThing
  {
    private static readonly int MaxCoolThings = 3;
    private static int ThingsAllocated = 0;
    private int thingNumber;

    public int ThingNumber
    {
      get { return thingNumber; }
    }

    public CoolThing(int tn)
    {
      if (ThingsAllocated == MaxCoolThings)
      {
        throw new ApplicationException("No free CoolThings");
      }
      thingNumber = tn;
      Console.WriteLine("Thing {0} allocated", thingNumber);
      ++ThingsAllocated;
    }

    ~CoolThing()
    {
      Console.WriteLine("Thing {0} released", thingNumber);
      --ThingsAllocated;
    }
  }

}

The CoolThing constructor first checks whether any CoolThings are available. If so, it increments the number of allocated things and returns. If no CoolThings are available, the constructor throws an exception. The finalizer (destructor) decreases the number of allocated things, which simulates returning an item to a resource pool.

If you compile and run the program, your output will look something like this:

Thing 0 allocated
Hello from Thing 0
Thing 1 allocated
Hello from Thing 1
Thing 2 allocated
Hello from Thing 2
Exception: System.ApplicationException: No free Things
  at AllocExample.CoolThing..ctor() in d:\allocexample\coolthing.cs:line 23
  at AllocExample.Class1.Main(String[] args) in d:\allocexample\class1.cs:line 15
Press Enter to exit program
Thing 0 released
Thing 2 released
Thing 1 released

Even though the allocated things go out of scope, no garbage collection occurs, so the finalizers are not called. The result? A resource leak that causes the program to crash.

This is a simplified example, but not a contrived example. A program that searches a directory for files that contain a particular text string would operate in much the same way, as would a program that allocates window handles (yes, you still can do that in .NET) or other Windows API objects.

So What's the Point?

The point is that programs that make use of objects that allocate scarce resources must take special care to ensure that the resources allocated by those objects are released as quickly as possible. But how? You can't call the object's finalizer, and forcing a garbage collection every time you release a resource would make your program unimaginably slow. The only reasonable solution is to create an object "de-initialization" method to release the scarce resource. The object itself remains intact, but the resource that it had allocated is returned to the resource pool. Listing 3 shows how this would be done.

Listing 3 Implementing a Disposal Method

private bool disposed=false;
public void Dispose()
{
  if (!disposed)
  {
    --ThingsAllocated;
    disposed = true;
  }
}

~CoolThing()
{
  Console.WriteLine("Thing {0} released", thingNumber);
  Dispose();
}

You would have to add some additional logic to every method that accesses the resource so that you could throw an exception if a program tries to access a resource that has been disposed. Modifying the loop in Listing 1 to use this new disposal method is very simple, as shown in Listing 4:

Listing 4 Using the New Dispose Method

for (int i = 0; i <= 3; i++)
{
  // allocate a Thing and use it
  CoolThing ct = new CoolThing(i);
  try
  {
    // do stuff with the Thing
    Console.WriteLine("Hello from Thing {0}", ct.ThingNumber);
    // Thing goes out of scope here and can be released
  }
  finally
  {
    ct.Dispose();
  }
}
  • Share ThisShare This
  • Your Account

Discussions

Make a New Comment

You must log in in order to post a comment.

Related Resources

Jennifer  BortelWin FREE iPhone Developer Books and Videos- Introducing @InformIT Giveaways
By Jennifer Bortel on February 5, 2010 No Comments

Apples’s recent iPad announcement made our hearts flutter so we couldn’t resist making an announcement of our own!

Today marks the first ever @InformIT Giveaway!

We’ll regularly post a video like this one profiling spectacular prizes we’re giving away—from books and videos to T-shirts and other exciting stuff. Check out the video below to see the giveaways for today, and then scroll down for more prize details and instructions on how to win them!

Dustin Sullivan"Every OSX developer should have this book on their desk."
By Dustin Sullivan on February 1, 2010 No Comments

That was the sentence Mike Riley ended his recent Dr Dobb's CodeTalk review of Cocoa Programming Developer's Handbook with.

David ChisnallCocoa Tip of the Day, 1/29/10
By David Chisnall on January 29, 2010 No Comments

Don't ignore old versions of OS X.

See All Related Blogs

Informit Network