• Shortcuts : 'n' next unread feed - 'p' previous unread feed • Styles : 1 2

» Publishers, Monetize your RSS feeds with FeedShow:  More infos  (Show/Hide Ads)


Date: Friday, 23 Mar 2007 22:42

Tired of the same old “garbage”? Do you want to be the tester for one of the most challenging technical areas of the Common Language Runtime (CLR) and work with the industry’s top developers, architects, and distinguished engineers? The CLR team is looking for a highly motivated and outstanding technical person to test the world class CLR Garbage Collector and help make it better. If you are interested in figuring out new ways to drive how the CLR GC operates under complex technical scenarios, such as in Exchange Server, SQL Server, ASP.NET, and WPF (Windows Presentation Framework), come work for the CLR GC team! 

Edit: Updated link

Edit: Position has been filled.  Thanks!

Author: "clyon" Tags: "Misc"
Comments Send by mail Print  Save  Delicious 
Date: Monday, 12 Mar 2007 22:10

As you may know, there are different GC modes to choose from depending on the type of application you’re using:  Server GC, Workstation GC, and Concurrent GC (more info).  These settings are process-wide, set at the beginning of the process.  Once the GC mode is set, it cannot be changed. 
In Orcas, we’ve added the concept of GC Latency Modes that while process-wide, can be changed during the lifetime of the process to meet an application’s needs.
The Latency Modes can be accessed as new properties onto the GCSettings class:

System.Runtime.GCLatencyMode System.Runtime.GCSettings.LatencyMode { get; set; }

The values for GCLatencyMode are Batch, Interactive and LowLatency.

  • Batch:  This mode is designed for maximum throughput, at the expense of responsiveness.   It is best for applications with no UI or server-side operations and is equivalent to Workstation GC without Concurrent GC.   If Concurrent GC is enabled, switching to Batch mode will prevent any further concurrent collections.  This is the only valid mode for Server GC.
  • Interactive:  This mode balances responsiveness with throughput.  It is designed for applications with UI and is the default Latency Mode, equivalent to Workstation GC with Concurrent GC.   This mode is not available on Server GC.  
  • LowLatency:  This mode is meant for short-term, time-sensitive operations where interruptions from the GC may be disruptive, like animation rendering or data acquisition functions.  This mode is not available on Server GC.

How does LowLatency mode work?

When you set the latency mode to LowLatency, the GC will perform almost no generation 2 collections, nor will it start any new concurrent collections.  Since generation 2 is unbounded and can become very large, collecting it can cause your managed threads to pause for short amounts of time.  This can be unacceptable for certain scenarios. 

To be clear, I’m not talking about real-time application requirements, rather requirements that a short-running block of code run smoothly with minimal interruptions from the runtime.  LowLatency mode is not real-time mode.

I mentioned above that in LowLatency mode, the GC will perform almost no most generation 2 collections, but there are situations when it will.  As we know, there are three things that cause the GC to perform a collection (more info):

  1. Allocation exceeds the Gen0 threshold – normally, these collections can escalate into full heap collections.  With LowLatency, generation 1 is the maximum generation that will be collected, possibly promoting objects to generation 2.
  2. System.GC.Collect is called – this will continue to work as expected.  If you specify to collect generation 2, the GC will honor your request regardless of the Latency Mode.
  3. System is in low memory situation – the OS has raised an event telling the runtime that it is low on system memory.  In this ase the GC will perform a generation 2 collection to attempt to free memory.  The alternative is to allow the OS to begin paging which will generally have worse pause times than a full collection.

How to safely use LowLatency mode

As you might have guessed, since generation 2 is rarely collected, OutOfMemoryExceptions are more likely under LowLatency mode.  Here are some guidelines to follow to avoid potential problems:

  • Keep the amount of time spent in LowLatency as short as possible.  Remember, you’re changing the behavior of the GC, which can lead to sub-optimal performance in the long-run. 
  • While in LowLatency mode, minimize the number of allocations you make, in particular allocations onto the Large Object Heap and pinned objects.
  • Be mindful of other threads that could be allocating.  Remember, these settings are process-wide, so you could generate an OutOfMemoryException on any thread that may be allocating.
  • Wrap the LowLatency code in a CER (more info).
  • Remember to set the latency mode back to avoid hard-to debug OutOfMemoryExceptions later.

Here’s a code sample of how to use LowLatency mode

// preallocate objects here
GCLatencyMode oldMode = GCSettings.LatencyMode;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
    GCSettings.LatencyMode = GCLatencyMode.LowLatency;      

    // perform time-sensitive actions here

    /*
    minimize:
    -all allocations, especially LOH allocations
    -pinning
    -allocations on other threads
    */
}
catch (ApplicationException)
{
    // catch any exceptions you expect your application to throw
    // perform cleanup code
}
finally

    // always set the mode back! 
    GCSettings.LatencyMode = oldMode;
}

Remember, this mode can cause failures in your application, so please use good judgment when using it.

Author: "clyon" Tags: "GC, Orcas"
Comments Send by mail Print  Save  Delicious 
Date: Thursday, 08 Mar 2007 01:14

In Orcas we’ve added an overload to System.GC.Collect():

void System.GC.Collect(int generation, System.GCCollectionMode mode)

Where generation is the highest generation to collect (from 0 to System.GC.MaxGeneration) and mode can be:

  • Default:  the same behavior if you called GC.Collect without specifying the mode.  Currently this is the same behavior as Forced, but this is subject to change in future versions of the runtime.
  • Forced:  guarantees a collection occurs for all the generations up to and including generation. 
  • Optimized:  this mode will tell the GC to only collect if it determines that a collection will be productive.  In this case, “productive” is determined by a number of factors, including amount of memory considered garbage, heap fragmentation, etc.  The exact formula is subject to change between CLR releases.  If the GC decides a collection will not be productive, then the call will have no effect.

When should we use these new modes?

Calling GC.Collect is generally discouraged, but as Rico points out here, there are legitimate circumstances where you know there is a large number of objects you’ll never need again.  For example, at the end of a game level, when a custom Form is closed, or when a web form is finished, there may be a number of long-lived objects in generation 2 that are now dead.  By calling an Optimized collection at this point you give the GC a chance to evaluate the heap, and have it decide if a collection will free enough memory to be worth it.

Forced and Default modes should generally only be used for debugging or testing scenarios, where you want to ensure objects are collected at a certain point in your application, or want to compare performance data.  In future versions of the CLR, there may be new guidance for using Default, but for now, its use is discouraged.

Author: "clyon" Tags: "GC, Orcas"
Comments Send by mail Print  Save  Delicious 
Date: Tuesday, 06 Mar 2007 08:14

The Orcas March CTP is out, and what does that mean for the Garbage Collector?  The GC team has been concentrating on three areas for this release:

  1. Bug fixes.  For Orcas, we’ve fixed several premature Out of Memory bugs, improved stability in certain stressful conditions, and even improved performance in some scenarios.  
  2. GC Collection Modes.  We’ve added a new overload to System.GC.Collect that takes a System.GCCollectionMode enum, which allows the user to either force a collection or allow the GC to decide if a collection would be productive.
  3. GC Latency Modes.  A new GC feature that allows the user to specify blocks of code that are time-sensitive, and the GC will minimize its intrusiveness.   This has been implemented as new properties on the System.Runtime.GCSettings class.

In my next few blog entries I’ll go into more detail on our new features, including code samples and best practices.

Author: "clyon" Tags: "GC, Orcas"
Comments Send by mail Print  Save  Delicious 
Date: Monday, 28 Aug 2006 21:05

The purpose of GC.KeepAlive(Object) is to tell the GC not to collect an object until a certain point.  For example:

class MyObject
{
   ~MyObject()
   {
   Console.WriteLine(“MyObject Finalized”);
   }             

   public static void Main()
   {
      MyObject obj = new MyObject();
      LongRunningMethod();
      GC.KeepAlive(obj); // ~MyObject will NOT be run before this call
      ...
   }
}

KeepAlive will ensure ~MyObject will not get run before LongRunningMethod gets called.  This is useful if the long running method passes the object out to unmanaged code, for example.  In that case, you’ll want to keep the object from being collected by the GC until the method returns.

What’s the secret to KeepAlive?  Nothing.  It’s just a normal method with no side effects except holding a reference to an object.  Since it holds a reference, the JIT considers the object rooted until that point (if no other reference to this object exists and if you are not in debuggable code), and the GC will not collect it.

So when does KeepAlive not keep an object alive?  When it’s not called.  Ok, that was deliberately cryptic, let me illustrate using the MyObject class above: 

public static void Main()
{
   MyObject obj = new MyObject();
   while (true) 
   {
      GC.Collect(); // force a collection to illustrate
   }
   GC.KeepAlive(obj);
   ...
}

One would expect that when run, there would be nothing printed to the screen, since KeepAlive keeps obj from getting collected.  But since KeepAlive is after a while(true) loop it’s actually unreachable.  Compiling the code will give you compiler warning CS0162: Unreachable code detected on the GC.KeepAlive(obj) line.  Since KeepAlive is not even being called, obviously it won’t hold the object live. However if you compile the code in debug mode, the JIT will extend lifetimes of references to the end of their enclosing method.  In this case, KeepAlive actually isn’t necessary.  But in the release case, a reference should be live until the last line of code that references it.  So why is obj getting finalized?

Looking at the IL for Main, we see what happened:

.method public hidebysig static void  Main() cil managed
{
  .entrypoint
  // Code size       21 (0x15)
  .maxstack  1
  .locals init ([0] class MyObject obj,
           [1] bool CS$4$0000)
  IL_0000:  nop
  IL_0001:  newobj     instance void MyObject::.ctor()
  IL_0006:  stloc.0
  IL_0007:  br.s       IL_0011
  IL_0009:  nop
  IL_000a:  call       void [mscorlib]System.GC::Collect()
  IL_000f:  nop
  IL_0010:  nop
  IL_0011:  ldc.i4.1
  IL_0012:  stloc.1
  IL_0013:  br.s       IL_0009
} // end of method MyObject::Main

Since the code after the while loop is considered dead, the compiler has actually not built it.  So the call to KeepAlive was optimized away, and is never actually called by the runtime.  The JIT then thinks it is no longer reachable after the while loop, and the GC is free to collect it.

Author: "clyon" Tags: "GC"
Comments Send by mail Print  Save  Delicious 
Date: Tuesday, 02 May 2006 03:23

The WeakReference class has two public constructors. 

 

public WeakReference(Object target)

public WeakReference(Object target, bool trackResurrection)

 

The first parameter is pretty obvious.  It’s the object you want the WeakReference to reference, without keeping the object alive.  If a WeakReference is the only thing referencing an object, then the GC is free to collect the object.  After the GC collects the object, WeakReference.IsAlive will return false, and WeakReference.Target will return null.  The WeakReference loses its handle to the object as soon as the GC collects it but before the finalizer (if any) gets run.  This is called a short WeakReference.

 

If the caller passes true as the trackResurrection parameter, then the WeakReference tracks the object’s life until finalization is complete.  This is called a long WeakReference.  If the object is resurrected, a long WeakReference will continue to reference it, where a short WeakReference will report it as dead.

 

The same distinction also applies to GCHandleType.Weak.

Author: "clyon" Tags: "WeakReferences"
Comments Send by mail Print  Save  Delicious 
Date: Wednesday, 26 Apr 2006 03:38

I’m sure many of you have heard the term “object resurrection” with respect to the GC.  It’s an interesting (but not very useful) way to illustrate object lifetimes and the role of finalization versus garbage collection.  Basically, it’s a way to reference an object that has been finalized.

 

Here’s a rough description of how object resurrection can occur:

 

  1. A finalizable object is created.  It reachable from user code and is considered “live”. 
  2. Inside the object’s finalizer is a statement assigning the “this” pointer to a global object (like a static).  Since it has a finalizer, a reference to the object is put on the finalization watchlist by the GC.
  3. At some point the object’s last strong reference is gone, the object is considered garbage. The object reference is then moved off the finalization watchlist and onto the freachable queue.  Now the object is considered live again since it’s referred to by the freachable queue. and the object is considered “freachable”.
  4. When the finalizer thread runs the object’s finalizer, the object is removed from the freachable queue.  The global object now points to the object, making it once again reachable from user code.  The object is said to have been “resurrected” and is once again reachable from user code and considered “live”.

 

Here’s a code sample to illustrate:

public class ResurrectedObj

{

 

      ~ResurrectedObj()

      {

            // this will resurrect the object by assigning

// it to the static reference

            resurrectedReference = this;

      }

 

      public static ResurrectedObj resurrectedReference = null;

 

      public static void Main()

      {

            ResurrectedObj liveReference = new ResurrectedObj();

            liveReference = null;

            GC.Collect();

// liveReference is now dead and the object is put on the

// freachable queue

            GC.WaitForPendingFinalizers();

            // at this point, the object previously referenced

// by liveReference is held alive by resurrectedReference

      }

}

 

Unfortunately there are several implications to object resurrection which may not be immediately obvious.  For these reasons we strongly recommend against resurrecting objects.

 

  • Once the object has been resurrected, the GC has removed it from the finalization queue.  This means when the object dies again, the finalizer will not be run a second time.  To ensure the finalizer gets run, a call to GC.ReRegisterForFinalization() is required.
  • Attempting to use an object after the finalizer has been run, can result in undefined behavior.  Maybe the finalizer released an unmanaged resource, and you try to access it?  This is the same reason why you are discouraged from accessing other managed objects from a finalizer: that object’s finalizer may have been run before yours.
  • Any object referenced by your resurrected object will also be resurrected.  If any of those objects are finalizable, their finalizers may have been run, and may be in an invalid state.
  • As with all finalizable objects, multiple garbage collections are required to completely clean up the object, potentially hurting performance and memory usage.
Author: "clyon" Tags: "GC"
Comments Send by mail Print  Save  Delicious 
Date: Friday, 21 Apr 2006 03:34

The WeakReference class has the property IsAlive.  The problem with it, is that you can only trust it if it returns false.

 

While a WeakReference points to an object that is either live (reachable), or garbage (unreachable) that has not yet been collected by the GC, the IsAlive property will return true.  After an object has been collected, if the WeakReference is short (or if the target object does not have a finalizer) then IsAlive will return false.  Unfortunately by the time IsAlive returns, the target may have been collected.

 

This situation can occur because of the way the GC suspends all managed threads before scanning the heap for garbage and collects it (this is an oversimplified explanation for illustrative purposes).  The GC can run at any time between two instructions.  For example:

 

Foo f = new Foo();

WeakReference wr = new WeakReference(f);

// code goes here ...

if (wr.IsAlive)

{

      // Garbage Collection may have occurred here!

      Foo f2 = (Foo)wr.Target;

      Console.WriteLine(f2.ToString());

}

 

If a collection occurred inside the if block, but before the WriteLine, this code would throw a NullReferenceException, since there are no live strong references to f.  The only reliable information IsAlive can give you, is that the object is no longer alive since once it’s dead, it’s dead (even resurrecting the target won’t make IsAlive return true once it thinks the object is dead).

 

The correct pattern for the above code looks like this:

 

Foo f = new Foo();

WeakReference wr = new WeakReference(f);

// code goes here...

Foo f2 = (Foo)wr.Target; // new strong reference

if (f2!=null)

{

      Console.WriteLine(f2.ToString());

}

 

So how is IsAlive useful at all?  You could imagine implementing a cache by using a collection of WeakReferences, and using the IsAlive property to determine if an object still exists, and if not, replenish the cache.  Just be careful to only make decisions based on IsAlive returning false.

Author: "clyon" Tags: "WeakReferences"
Comments Send by mail Print  Save  Delicious 
Date: Thursday, 14 Apr 2005 23:39

Joe Duffy has posted the revised Dispose, Finalization, and Resource Management Design Guideline. It's a great (and long) read. Check it out!

Author: "clyon" Tags: "Dispose"
Comments Send by mail Print  Save  Delicious 
Date: Friday, 18 Mar 2005 20:43

I've heard several people asking why GCHandle doesn't implement IDisposable, considering it wraps an unmanaged resource (a handle) and needs to be explicitly freed (using GCHandle.Free()). Before I explain the reason, I want to give a little background on GCHandles and their dangers.

What's a GCHandle?

A GCHandle is a struct that contains a handle to an object. It's mainly used for holding onto a managed object that gets passed to the unmanaged world to prevent the GC from collecting the object. You can also create a Pinned GCHandle to a managed object and retrieve the object's address in memory.

How are GCHandles dangerous?

When you create a new GCHandle, a new entry in the AppDomain's handle table is created. This entry is kept until the handle is freed (via GCHandle.Free()) or the AppDomain is unloaded.

Things get tricky if you were to make a copy of a GCHandle:

Object obj = new Object();
GCHandle gch = GCHandle.Alloc(obj, GCHandleType.Normal);
GCHandle gch2 = gch;

Since GCHandle is value type, gch2 has its own copy of the handle. You now have two handles that point to the same entry in the handle table. Unfortunately, since gch2 is a copy of –not a reference to– gch, anything that happens to gch doesn't happen to gch2. For example, calling gch.Free() will delete the entry from the handle table, but not update gch2, so gch2.IsAllocated will return true, but gch2.Target will be null. The same problem arises with casting to and from IntPtrs, and when GCHandles get boxed. Unlike double-freeing a single GCHandle, freeing the copy will NOT throw an InvalidOperationException. You have to be very careful not to double-Free your handles since this can corrupt the handle table.

Why don't GCHandles implement IDisposable?

One of the main purposes of IDisposable to avoid the use of finalizers. This is because finalizers are not run deterministically, and result in promoting a finalizable object a generation, effectively keeping in memory longer. Since GCHandle is a value type, it has no finalizer, and is not collected by the GC, so these problems are eliminated. Another other main use of IDisposable is to clean up unmanaged resources as soon as you are done with them. With a GCHandle, the resource is the handle which is cleaned up by calling GCHandle.Free(). If Free isn't called, the handle gets cleaned up when the appdomain is unloaded.

One of the side effects of having a struct implement IDisposable, is that users may be tempted to cast their GCHandles as IDisposable, which boxes the GCHandle into an IDisposable object on the heap, and the two GCHandles get out-of-sync. The same problem arises with the putting a disposable struct into a using block:

struct Test : IDisposable
{
    public bool disposed; // initialized to false
    public void Dispose()
    {
        disposed = true;
    }
}

public void Foo()
{
    Test t = new Test();
    using (t)
    {
        // do stuff
    }

    if (!t.disposed)
    {
        t.Dispose();
    }
}

t.disposed will return false, since it was a copy of t whose Dispose method was called. If t were a GCHandle, then the handle would be removed from the appdomain's handle table, and calling Free after the using would result in a double Free, even though IsAllocated would return true!

Remember, GCHandles are advanced structures, and one should be very careful to ensure they are cleaned up properly. Unfortunately, IDisposable makes it easy to get this wrong, so the BCL designers erred on the side of caution, and gave GCHandle a Free() method to use.

Author: "clyon" Tags: "Dispose, GCHandles"
Comments Send by mail Print  Save  Delicious 
Date: Wednesday, 16 Mar 2005 20:24

A great post on the BCL Team Blog by Brian Grunkmeyer about SafeHandles. He explains in-depth the topic I brought up ago a few posts ago about mitigating race conditions with the finalizer. Definitely worth a read.

Author: "clyon" Tags: "Misc"
Comments Send by mail Print  Save  Delicious 
Date: Friday, 04 Feb 2005 22:05

I posted previously about how to set the GC mode your application.  So now that you’re running your app, how do you know it’s running in that GC mode?

If you’re using v1.0 or v1.1, the CLR loads a different dll based on which GC mode (mscorwks.dll for workstation, mscorsvr.dll for server).  You can use tasklist.exe (ships with Windows XP and up) or tlist.exe (ships with Windows Debugging Tools) to determine which processes are using which dll.  To list all the applications running with server GC:

   tasklist /m mscorsvr.dll
   or 
   tlist /m mscorsvr.dll

In Whidbey, we’ve condensed all the GC code into one library: mscorwks.dll.  This means that all managed applications load mscorwks.dll, making the old method useless.  Luckily we have two new ways to determine the GC mode:

System.Runtime.GCSettings.IsServerGC will return true if we’re in server GC mode, and false if in workstation.  Note, this API is in a different location from Whidbey Beta 1.

The other way is using SOS, the debugger extension to WinDBG and Visual Studio.  When your assembly is running, you can query the GC mode with:

   !EEVersion

One thing to note is that this will only return the GC mode and the number of heaps after the heap (or heaps) has been initialized.

Author: "clyon" Tags: "GC, Server GC"
Comments Send by mail Print  Save  Delicious 
Date: Wednesday, 01 Dec 2004 18:23

GC Myth: setting an object's reference to null will force the GC to collect it right away.
GC Truth: setting an object's reference to null will sometimes allow the GC to collect it sooner.

As much as you may want to, you can't guarantee the GC will collect what you want, when you want it to. The best you can do is give it hints. The GC typically does collections when an allocation fails, not necessarily as soon as you're done with an object.

Consider the following method. What is the earliest point the GC could collect the Object that obj references?

void Foo() {
    Object obj = new Object();
    Console.WriteLine(obj.ToString());
    for (int i=0; i<10; i++) {
        Console.WriteLine(i);
    }
    obj = null;
}

The answer depends on whether you've compiled it in Debug or Release mode. In Debug, the JIT extends all references' lifetimes to the end of their scope. This is so you don't have to worry about the GC cleaning up a variable you're trying to inspect!

In Release mode, the Object is actually first eligible for collection after the call to ToString(). The JIT is usually smart enough to realize that obj = null can be optimized away. That leaves obj.ToString() as the last reference to the object. So after the call to ToString(), the obj is no longer used and is removed as a GC Root. That leaves the Object in memory orphaned (no references pointing to it), making it eligible for collection. When the GC actually goes about collecting it, is another matter.

So when does setting a reference to null allow the object in memory to be reclaimed sooner?

Consider instance variables. Imagine a class that contains a large array of a managed type. There could be times where you no longer need that array, but you want the object to hang around. This is a good case for the Dispose pattern:

class BigObject : IDisposable {
    int[,] array = new int[1024,1024];
    public void Dispose() {
        array = null;
    }
}

Now the GC will clean up the array when it does its next collection, and you can keep that BigObject around for whatever reason, and not worry about it taking up so much memory.

Author: "clyon" Tags: "GC"
Comments Send by mail Print  Save  Delicious 
Date: Monday, 27 Sep 2004 23:27

Maoni posted Part 2 to Using the GC Efficiently.  A very in-depth article about Server, Workstation and Concurrent GC.  Check it out.

Author: "clyon" Tags: "GC"
Comments Send by mail Print  Save  Delicious 
Date: Thursday, 23 Sep 2004 16:50

Due to the positive response on my previous entry on Dispose, I thought I'd write another, this time on what one should and shouldn't do with a Dispose method.

  • Dispose should contain code to release any unmanaged resources associated with the object's instance. For example, any handles should be released, database connections closed, etc. This simulates deterministic finalization by letting the developer do the resource cleanup when he/she is done with the object. You may want to use an IsDisposed bool and check it inside Dispose.
  • Dispose should be safely callable multiple times. In my example above, if some user of your class called Dispose twice, we would crash with an unhandled InvalidOperationException trying to “double free” the GCHandle.
  • If you inherit from an IDisposable object, call the base class' Dispose method. Unlike finalizers, which the GC calls, Dispose is only called by user code (sometimes automatically in C#, as I covered previously). This means any unmanaged resources held by your object's parent, must also be disposed of.
  • Suppress finalization in Dispose. There has been some debate about whether GC.SuppressFinalization(this) should go before or after the cleanup code. Some argue after, in case something goes wrong before you've finished cleaning up. Others argue before, otherwise you may open up a race condition between your finalizer and your Dispose method. You should consider both strategies depending on your object's implementation.
  • Don't throw exceptions in Dispose. Nothing should go wrong with your object calling Dispose. Mainly for reasons stated above.
  • Throw ObjectDisposedException if a caller tries to use a released resource. You may be in a situation where you've disposed of your object because you no longer need the unmanaged resource, but you still need to have the object hanging around. If this is the case, any methods that would normally access the resource should throw an ObjectDisposedException, while other (possibly useful) methods should not.
  • Don't call Dispose from one thread, while still using the object on another. Try to make the thread that created the object be the one that disposes of it. This is more general good multithreaded programming advice, then Dispose specific.
  • Whenever possible, follow the Dispose Pattern. That link, one more time: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconfinalizedispose.asp.

If you have any other Dispose Do's and Don'ts, please post them in the comments.

Author: "clyon" Tags: "Dispose"
Comments Send by mail Print  Save  Delicious 
Date: Tuesday, 21 Sep 2004 16:47

One commonly misunderstood feature is the relationship between disposable objects (Objects that implement the IDisposable interface) and the GC.  I know there are a lot of online resources about patterns and best practices but there is still a lot of confusion.  I remember going to a Dev Lab to answer some questions by Whidbey early adopters, and being asked at what point in an object’s lifetime does the GC call Dispose.  The answer: Never.

Dispose is just a method, like any other.  Its purpose is allow deterministic resource cleanup, much like C++’s destructors, but without freeing the object’s memory.  Like a C++ destructor, the developer puts whatever cleanup code is necessary to be performed when the object’s lifetime is up.  Unlike a destructor, the object lives on after being "disposed of".  This means an object may be in an invalid state after being disposed, and developers should consider using ObjectDisposedExceptions when methods in a disposed object are called.

What happens when you call Dispose()

Any cleanup your object need done in a timely fashion should be done in Dispose.  Things like closing database connections, closing files, releasing bitmaps, etc.  Unmanaged resources in particular should be released in Dispose.  This is because the GC has no knowledge of anything not allocated on the managed heap.  For example, a Bitmap object that encapsulates a 2MB image file reports only its managed size (the size of the managed object) to the GC.  The GC knows nothing about the 2MB unmanaged image, thus will not collect it from memory.  By calling Dispose, you tell the Bitmap object that you are finished with it, and it will release the image itself.

What doesn’t happen when you call Dispose()

  • Calling Dispose does not prioritize the object for garbage collection. It simply unloads the object’s (unmanaged) resources from memory.
  • Calling Dispose does not deallocate the object from memory.  Only the GC does that when it performs a collection of the generation in which the object resides.
  • The CLR does not insert or run any code not in Dispose.  The behaviour of Dispose is defined by the developer.
  • Dispose must be called explicitly by the application.  It is never called by the runtime.  The only exceptions are when using C#’s using statement (see ShawnFa’s article for a good explanation of what exactly goes on), and the foreach keyword in C# will result in Dispose being called on the Enumerator
  • Dispose is NOT threadsafe.  This means two threads can call Dispose on the same object at the same time.  Like for any other synchronization-sensitive method, take steps to make sure this doesn’t happen.  I’ll give an example of when this might happen in the next section.


What about the finalizer?

There are a few reasons why not to rely on the finalizer to clean up resources that I’ll cover in a future blog entry on finalizers.  The main reasons you should be concerned with are performance and determinism.  Finalizers are expensive to run, and there’s no order (or even guarantee) that they will be run (for example, the finalizer thread may time out, or be killed on AppDomain unload).

That being said, the finalizer should be your last chance to clean up resources, in case someone using your class forgets to dispose of it when done.  A call to Dispose inside the finalizer reduces duplication of code.  However, you want to make sure the resources don’t get released twice.   In your Dispose method, you want to make sure you call GC.SuppressFinalize(this) after your clean up code.  If you suppress the finalizer before the clean up, you’re limiting your ability to recover from failures during the cleanup.

Some of you may have noticed a race condition in my description above.  Consider a situation where the call to Dispose is the last time your object is being referenced.  As soon as Dispose is entered, the object is eligible for collection by the GC.  Before that happens, the object’s finalizer gets called, which calls Dispose.  You now have two threads inside Dispose, possibly double-freeing resources (this could be bad, as in the case of GCHandles).  Make sure you follow the pattern I linked to above and use a disposing flag to avoid problems like this.

For more information about an object being collected while one of its methods are being run, check out these (very complete) blog posts by Chris Brumme: (Finalization and Lifetime, GC.KeepAlive, handle recycling).

If your .NET application is using unmanaged resources, make sure to implement the Dispose pattern, secure in the knowledge that nothing magical is going on.

EDIT: Added point about Dispose and foreach. Thanks Steve!

Edit:  Minor corrections.

Author: "clyon" Tags: "Dispose"
Comments Send by mail Print  Save  Delicious 
Date: Friday, 17 Sep 2004 18:58

A GCHandle is a struct used to hold onto a managed object to be used by unmanaged code.  With a GCHandle you can (among other things):

  • Prevent an object from being garbage collected if unmanaged code has the only live reference to it
  • Pin an object in memory, so it won’t be relocated in memory by the garbage collector
  • Get the memory address of the pinned object

The last point is interesting.  When dealing with managed code, addresses of reference objects (objects allocated on the heap) are not constant.  As the GC tries to reclaim memory, it moves objects around in memory and compacts the heap.  If you’re P/Invoking into an unmanaged DLL, you may need to pass the address of some object.  That’s where GCHandles come in.

The syntax for allocating a pinned GCHandle is as follows (C# code):

Int[] arr = new int[10];
GCHandle gch = GCHandle.Alloc(arr, GCHandleType.Pinned);

The above code pins arr in memory (so it won’t be moved), and creates a new GCHandle that “wraps” arr.  You can now get the address of arr using GCHandle.AddrOfPinnedObject.

Note: you can only pin blittable types in memory (types that have the same representation in managed and unmanaged code).  Blittable types include primitive types and arrays.

The problem occurs when you accidentally misuse Alloc.  For example, I’ve seen code like this:

Object obj = new Object();
GCHandle gch2 = GCHandle.Alloc(Marshal.SizeOf(typeof(obj)), GCHandleType.Pinned);

It looks like the developer tried to allocate a GCHandle with the same size as obj in memory.  So what does this code actually do?

Marshall.SizeOf returns an int that when passed to a method expecting an Object, is boxed into a newly heap-allocated Object.  So the new GCHandle obediently pins this new Object in memory.  Then when you pass gch2.AddrOfPinnedObject to your unmanaged code… trouble.

Consider an unmanaged method takes an array of ints, and increments each element.  You’ve passed it an address, and it dutifully increments each value it finds for the length of the array.  Congratulations, you’ve just corrupted memory.

If you’re lucky, this crashes the runtime right away.  If you’re unlucky, your app may continue to run and crash sometime in the future or your app’s data may be messed up.

Author: "clyon" Tags: "GC, GCHandles"
Comments Send by mail Print  Save  Delicious 
Date: Tuesday, 14 Sep 2004 17:22

I keep a GC folder in my Favorites full of links to articles and blog posts about the .NET GC.  I thought it would be a good idea to consolidate them all into one blog post, for handy reference.  These are the articles I most often post as answers to questions on public newsgroups.
 
If you know of any other good ones, please post them in the comments!  If you find some totally erroneous articles, point them out too.

GC Overview and Best Practices:
 
http://msdn.microsoft.com/msdnmag/issues/1100/gci/
http://msdn.microsoft.com/msdnmag/issues/1200/GCI2/default.aspx
Garbage Collection (parts 1 & 2): Automatic Memory Management in the Microsoft .NET Framework
Jeffrey Richter

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconfinalizedispose.asp
Implementing Finalize and Dispose to Clean Up Unmanaged Resources
.NET Framework General Reference

http://blogs.msdn.com/cbrumme/archive/2004/02/20/77460.aspx
Finalization
Chris Brumme

http://blogs.msdn.com/cbrumme/archive/2003/04/15/51319.aspx
Turning off the garbage collector
Chris Brumme

Performance:
 
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndotnet/html/dotnetGCbasics.asp
Garbage Collector Basics and Performance Hints
Rico Mariani

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt05.asp
Improving .NET Application Performance and Scalability: Chapter 5 — Improving Managed Code Performance
J.D. Meier, Srinath Vasireddy, Ashish Babbar, Rico Mariani, and Alex Mackman

http://weblogs.asp.net/ricom/archive/2003/12/04/41281.aspx
Mid-life crisis
Rico Mariani

http://weblogs.asp.net/ricom/archive/2003/12/02/40780.aspx
Two things to avoid for better memory usage
Rico Mariani

http://blogs.msdn.com/maoni/archive/2004/06/15/156626.aspx
http://blogs.msdn.com/maoni/archive/2004/09/25/234273.aspx
Using GC Efficiently - Parts 1 & 2
Maoni Stephens

Edit: Added link to Maoni's GC posts.

Author: "clyon" Tags: "GC"
Comments Send by mail Print  Save  Delicious 
Date: Friday, 10 Sep 2004 23:17

One of the most common “bugs” I read about on the Microsoft public newsgoups is the fact that the runtime does not automatically choose Server GC mode on a multi-proc machine, or server OS.

If the server OS is running on a single-proc machine then the runtime will have to load the Workstation GC, since Server GC doesn’t work on a single proc, so we’re down to just the multi-proc issue.

Let’s imagine for a minute that the runtime did choose Server GC on its own.

If Server GC is automatically enabled, then all applications, both applications running as services and user applications will use this GC mode. This is good news for the services, but bad news for the user applications.

Like I explained in my previous post, user applications are best run with Concurrent GC, to maximize responsiveness. Unfortunately, Server GC and Concurrent GC are incompatible, so the runtime has effectively made user applications perform worse on a multi-proc machine than they do on a single proc! And since there’s no way to specify which GC mode to use (in v1.0 and v1.1 pre-SP1), you’re stuck with services with optimized throughput, and laggy user interfaces. This really sucks if you happen to have bought a brand new machine with a Hyperthreaded processor, and want to run managed apps on it!

The CLR Team decided on behaviour with the least negative performance impact, so you either have to host your service if you want it run with Server GC, or use config files.

Author: "clyon" Tags: "GC, Server GC"
Comments Send by mail Print  Save  Delicious 
Date: Wednesday, 08 Sep 2004 18:48

One common question I see asked is the differences between server and workstation GC, and how Concurrent GC fits in.

Server GC is only available on multi-proc machines. It creates one GC heap (and thus one GC thread) for each processor, which are collected in parallel. This GC mode maximizes throughput (number of requests per second) and shows good scalability (performance really shines with 4 or more processors).

Workstation is the default GC mode. On a single-proc machine, it’s the only option.

Concurrent GC is used in Workstation mode on a multi-proc machine. It performs full collections (generation 2) concurrently with the running program, minimizing the pause time. This mode is particularly useful for applications with graphical user interfaces or applications where responsiveness is essential.

How do I choose a GC mode?

In v1.0 and v1.1 (pre-SP1), server mode can only be used if the runtime is hosted in an unmanaged application (for example, ASP.NET hosts web applications in server mode). Concurrent mode can be specified in the machine or application’s configuration file. If neither is chosen, or if on a single-proc machine, Workstation is the default.

Note: Server mode automatically disables Concurrent GC.

To set GC mode to Concurrent:

<configuration>
  <runtime>
    <gcConcurrent enabled="true" />
  </runtime>
</configuration>

To set GC mode to Server (unmanaged C++):

HRESULT CorBindToRuntimeEx( LPWSTR pwszVersion,
  LPWSTR pwszBuildFlavor, // use “svr” for server mode,
  // “wks” or NULL for workstation
  DWORD flags,
  REFCLSID rclsid,
  REFIID riid,
  LPVOID* ppv );

One of the most popular feature requests was the ability to specify the GC mode in a non-hosted managed application. In Whidbey (v2.0) and v1.1 SP1, we added a new feature that allows you to specify the GC mode in the application’s config file:

<configuration>
  <runtime>
    <gcServer enabled="true" />
  </runtime>
</configuration>

Note: if the application is hosted, the host’s GC mode overrides the config file.

How do I tell which GC mode my app is using?

In v1.0 and v1.1 of the CLR, the GC was contained in two core DLLs: mscorwks.dll and mscorsvr.dll. If the application is running in server mode, then mscorsvr.dll is loaded, otherwise, mscorwks.dll is loaded. The only way to tell which is loaded is to look at the list of running processes.

Remember, on a single proc-machine, it’s Workstation. On a multi-proc where throughput is important, use Server. If there's user interaction, choose Concurrent.

Edit: Minor corrections.

Edit: Fixed case in XML tags. Thanks Tyler!

Author: "clyon" Tags: "GC, Server GC"
Comments Send by mail Print  Save  Delicious 
Next page
» You can also retrieve older items : Read
» © All content and copyrights belong to their respective authors.«
» © FeedShow - Online RSS Feeds Reader