Welcome to MSDN Blogs Sign in | Join | Help

The GC Test Team is Hiring!

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! 

http://members.microsoft.com/careers/search/details.aspx?JobID=7F7FDD9C-C9CD-43A9-8C66-52CE67A0E96C

Edit: Updated link

Posted by clyon | 1 Comments
Filed under:

New In Orcas Part 3: GC Latency Modes

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.

Posted by clyon | 6 Comments
Filed under: ,

New In Orcas Part 2: GC Collection Modes

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.

Posted by clyon | 5 Comments
Filed under: ,

New In Orcas Part 1: What we’ve been doing

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.

Posted by clyon | 1 Comments
Filed under: ,

When GC.KeepAlive Doesn’t

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.

Posted by clyon | 6 Comments
Filed under:

WeakReferences And Tracking Resurrection

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.

Posted by clyon | 3 Comments
Filed under:

Object Resurrection

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.
Posted by clyon | 3 Comments
Filed under:

Why You Shouldn’t Rely On WeakReference.IsAlive

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.

Posted by clyon | 5 Comments
Filed under:

New Dispose Guidelines

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

Posted by clyon | 0 Comments
Filed under:

The Truth About GCHandles

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.

Posted by clyon | 6 Comments
Filed under: ,

Brian Talks About SafeHandles

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.

Posted by clyon | 0 Comments
Filed under:

How To Tell Which GC Mode Your Application Is Using

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.

Posted by clyon | 8 Comments
Filed under: ,

To Null or Not to Null

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.

Posted by clyon | 11 Comments
Filed under:

Part 2 in Maoni's Using GC Efficiently

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

Posted by clyon | 0 Comments
Filed under:

Dispose Dos and Don'ts

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.

Posted by clyon | 16 Comments
Filed under:
More Posts Next page »
 
Page view tracker