GC start and stop events
There's a much easier way if all you want to do is figure out when GC is running, it won't tell you exactly when it starts, nor at all when it ends, but if you can see the output from this method I'll describe here when you notice the pauses on your servers, you should be able to figure out if GC is your problem.
Basically, what you do is create a class with a finalizer, construct an object of that class and just drop the reference (ie. don't store it). The object will then be left until GC hits it, where it will be finalized.
The trick now is in the finalizer you log (in whatever way you want to use) that the finalizer has run, and unless the appdomain is in the process of shutting down, you simply construct a new object which you promptly drop the reference to, ready for the next GC.
This works surprisingly well and doesn't need much work from your part.
Here's the class I use:
namespace PresentationMode
{
/// <summary>
/// This class is used to get a running log of the number of garbage collections that occur,
/// when running with logging.
/// </summary>
public sealed class GCLog
{
#region Construction & Destruction
/// <summary>
/// Releases unmanaged resources and performs other cleanup operations before the
/// <see cref="GCLog"/> is reclaimed by garbage collection.
/// </summary>
~GCLog()
{
SiAuto.Main.LogMessage("GARBAGE COLLECTED");
if (!AppDomain.CurrentDomain.IsFinalizingForUnload() && !Environment.HasShutdownStarted)
new GCLog();
}
#endregion
#region Public Static Methods
/// <summary>
/// Registers this instance.
/// </summary>
public static void Register()
{
#if DEBUG
if (SiAuto.Si.Enabled)
new GCLog();
#endif
}
#endregion
}
}
All you have to do is call the .Register()
method. Note here that I use SmartInspect as my logging tool so you want to replace the calls involving SiAuto
with something else.
In another project, also using SmartInspect, which has the notion of 'watches', where you can send numeric values, and graph them in the logging tool, I sent the values 0, 1, and then 0 in rapid succession, as this would give me a graph that held at 0 at all times, but produced a sharp spike whenever there was a GC running. Couple this with a background thread that monitored CPU usage and memory used gave me very good data to work with.
This is what the Profiling API is for. See ICorProfilerCallback2::GarbageCollectionStarted and GarbageCollectionFinished.
As this is a profiler, it obviously isn't suitable for routine use on a production system. But it sounds like you're primarily interested in this for diagnostic purposes anyway. So it might be worth checking whether one of the commercial profilers offers this facility already, or whether the perfmon counters would suffice -- writing your own profiler could be an awfully heavyweight solution!