5

.NET を使用して Web ファームを実行しています。各 Web サーバーは、そのメモリ内にかなりの量の静的オブジェクトを保持しています。Gen 2 ガベージ コレクション (GC) には 10 ~ 20 秒かかり、5 分ごとに実行されます。StackOverflow が遭遇したのとほぼ同じ問題に遭遇しました: http://samsaffron.com/archive/2011/10/28/in-managed-code-we-trust-our-recent-battles-with-the-ネットガベージコレクター

現在、キャッシュ内のオブジェクトの数を減らしています。ただし、これには時間がかかります。

同時に、GC の接近について .NET で通知を受け取るために、ここに記載されているメソッドを実装しました。目標は、GC が近づいているときに Web サーバーをファームから取り出し、GC が終了した後にファームに含めることです。ただし、すべての GC の 0.7% についてのみ通知を受け取ります。maxGenerationThreshold と largeObjectHeapThreshold を 8 に設定しています。他のしきい値も試しましたが、失敗した GC の量は変わりませんでした。

同時サーバー ガベージ コレクションを使用しています ( http://msdn.microsoft.com/en-us/library/ms229357.aspx )。GCLatencyMode はインタラクティブです ( http://msdn.microsoft.com/en-us/library/system.runtime.gclatencymode.aspxを参照)。ここでも、他の GC モード (ワークステーション モード、バッチなど) を使用しようとしました。また、ほとんどの GC について通知を受け取りませんでした。

何か問題があるのでしょうか、それとも GC が発生するたびに通知を受け取ることができないのでしょうか? 通知の数を増やすにはどうすればよいですか?

http://assets.red-gate.com/community/books/assets/Under_the_Hood_of_.NET_Management.pdfによると、最初に Gen2 が ~10 MB に達すると GC がトリガーされます。大量の RAM があるため、このしきい値を手動でより高いレベルに設定できれば、このしきい値に到達するまでにより多くの時間がかかり、私の理解では、通知を受け取る確率が高くなります。このしきい値を変更する方法はありますか?

これは、通知を登録してリッスンするコードです。

GC.RegisterForFullGCNotification(gcThreshold, gcThreshold);
// Start a thread using WaitForFullGCProc.
thWaitForFullGC = new Thread(WaitForFullGCProc);
thWaitForFullGC.Name = "HealthTestGCNotificationListenerThread (Threshold=" + gcThreshold + ")";
thWaitForFullGC.IsBackground = true;

WaitForFullGCProc():

    private void WaitForFullGCProc()
{
    try
    {
        while (!gcAbort)
        {
            // Check for a notification of an approaching collection.
            GCNotificationStatus s;
            do
            {
                int timeOut = CheckForMissedGc() > 0 ? 5000 : (10 * 60 * 1000);
                s = GC.WaitForFullGCApproach(timeOut);
                if (this.GcState == GCState.InducedUnnotified)
                {
                    // Set the GcState back to okay to prevent the message from staying in the ApplicationMonitoring.
                    this.GcState = GCState.Okay;
                }
            } while (s == GCNotificationStatus.Timeout);

            if (s == GCNotificationStatus.Succeeded)
            {
                SetGcState(GCState.Approaching, "GC is approaching..");
                gcApproachNotificationCount++;
            }
            else
            {
                ...
            }

            Stopwatch stopwatch = Stopwatch.StartNew();
            s = GC.WaitForFullGCComplete((int)PrewarnTime.TotalMilliseconds);
            long elapsed = stopwatch.ElapsedMilliseconds;

            if (s == GCNotificationStatus.Timeout)
            {
                if (this.ForceGCWhenApproaching && !this.IsInGc && !this.IsPeriodicGcApproaching)
                {
                    this.IsInGc = true;
                    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, blocking: true);
                    GC.WaitForPendingFinalizers();
                    elapsed = stopwatch.ElapsedMilliseconds;
                    this.IsInGc = false;
                }
            }
        }
        gcAbort = false;
    }
    catch (Exception e)
    {
    }
}
4

1 に答える 1

3

注: これはコメントのようなものですが、大きなコード サンプルが含まれています。

別の方法で GC 通知を取得しようと考えたことはありますか? Jeffrey Richter (C# 経由の CLR) は、通知を取得する良い方法を説明しています。オブジェクトを使用し、そのファイナライザー メソッドがどの世代であるかをチェックします。

これはクラスです: 提供された世代が一致する場合に収集される内部オブジェクトを使用するか (new GenObject(0);例を参照)、次のより高い世代のために復活します。

そして、あなたはそれを購読するだけですGCNotification.GCDone += GCDoneHandler;

 public static class GCNotification
    {
        private static Action<Int32> s_gcDone = null; // The event's field
        public static event Action<Int32> GCDone
        {
            add
            {
                // If there were no registered delegates before, start reporting notifications now
                if (s_gcDone == null) { new GenObject(0); new GenObject(1); new GenObject(2); }
                s_gcDone += value;
            }
            remove { s_gcDone -= value; }
        }
        private sealed class GenObject
        {
            private Int32 m_generation;
            public GenObject(Int32 generation) { m_generation = generation; }
            ~GenObject()
            { // This is the Finalize method
                // If this object is in the generation we want (or higher),
                // notify the delegates that a GC just completed
                if (GC.GetGeneration(this) >= m_generation)
                {
                    Action<Int32> temp = Volatile.Read(ref s_gcDone);
                    if (temp != null) temp(m_generation);
                }
                // Keep reporting notifications if there is at least one delegate registered,
                // the AppDomain isn't unloading, and the process isn’t shutting down
                if ((s_gcDone != null)
                && !AppDomain.CurrentDomain.IsFinalizingForUnload()
                && !Environment.HasShutdownStarted)
                {
                    // For Gen 0, create a new object; for Gen 2, resurrect the object
                    // & let the GC call Finalize again the next time Gen 2 is GC'd
                    if (m_generation == 0) new GenObject(0);
                    else GC.ReRegisterForFinalize(this);
                }
                else { /* Let the objects go away */ }
            }
        }
    }
于 2013-02-22T07:32:17.610 に答える