2013-02-21 19 views
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-net-garbage-collectorガベージコレクションの通知がありません

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

同時に、私たちは、hereに記載されたメソッドを実装して、.NETでのGC接近に関する通知を取得しました。 目的は、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) 
    { 
    } 
} 
+0

でそれを購読しますか? – Alex

+0

'GC.RegisterForFullGCNotification(gcThreshold、gcThreshold); // WaitForFullGCProcを使用してスレッドを開始します。 thWaitForFullGC =新しいスレッド(WaitForFullGCProc); thWaitForFullGC.Name = "HealthTestGCNotificationListenerThread(Threshold =" + gcThreshold + ")"; thWaitForFullGC。 IsBackground = true; – kopernik

答えて

3

注:これは、コメントの多いが、大きなコードサンプルを含みます。

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

これはクラスです。内部オブジェクトは、提供された世代が一致する場合(たとえばnew GenObject(0);を参照)または次の世代の世代で復活した場合に収集されます。

そして、あなたはちょうどあなたがおそらくあなたが登録して、GCの通知をリッスンコードを投稿できる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 */ } 
      } 
     } 
    } 
+0

これは良いアイデアですが、残念ながら、GCが終了した後に通知するだけです。今後のガベージコレクションについて私に通知しません... – kopernik