Garbage Collectionを検知する(C#)

通常のプログラミングにおいて、いつGCされたか気にする必要はありませんが、極まれにGCのタイミングで処理をしたい時があります(設計で回避するべきではありますが…)

今回はGCを検知する方法を調べてみました。

GC.WaitForFullGCComplete

GCクラスの関数を利用する方法です。

これを利用するには、App.configで下記が指定されている必要があります。(コンカレントGCを無効化)

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


GCNotifierクラスを定義してみます。

GC.RegisterForFullGCNotificationで通知を有効化し、GC.WaitForFullGCApproachとGC.WaitForFullGCCompleteで通知を待ちます。

GC.RegisterForFullGCNotificationの引数は、大きいほど通知を受け取りやすくなるそうです。

static class GCNotifier
{
    public static event Action Collected;
    public static void Start()
    {
        GC.RegisterForFullGCNotification(10, 10);
        Task.Run(() =>
        {
            while (true)
            {
                GC.WaitForFullGCApproach();
                if (GC.WaitForFullGCComplete() == GCNotificationStatus.Succeeded)
                    Collected?.Invoke();
            }
        });
    }
}


利用は下記のようになります。

GCNotifier.Collected += () => Console.WriteLine("collected");
GCNotifier.Start();


Finalizeを利用

下記のものを簡略化してみます。
Jeffrey Richter: Receiving notifications when garbage collections occur – Microsoft Press blog

DummyクラスのFinalizeでコールバックを呼び、さらに新しいDummyオブジェクトを生成しています。

GCNotifierクラスは下記のようになり、利用方法はGC.WaitForFullGCCompleteの時と同じです。

static class GCNotifier
{
    public static event Action Collected;
    public static void Start()
    {
        new DummyObject();
    }
    class DummyObject
    {
        ~DummyObject()
        {
            if (!AppDomain.CurrentDomain.IsFinalizingForUnload()
            &&  !Environment.HasShutdownStarted)
            {
                Collected?.Invoke();
                new DummyObject();
            }
        }
    }
}


比較

GC.WaitForFullGCCompleteは、GCの後と前で処理を追加できるのが利点ですが、コンカレントGCが使えないのと、全てのGCで通知が来るわけではないのが欠点です。

Finalize利用は、全てのGCを検知できるのが利点ですが、GC前に処理が追加できないのが欠点です。