2013-02-21 65 views
5

Chúng tôi đang chạy một trang trại sử dụng .NET. Mỗi máy chủ web chứa một lượng đáng kể các đối tượng tĩnh trong bộ nhớ của nó. Một bộ sưu tập rác Gen 2 (GC) mất 10-20 giây và nó chạy mỗi 5 phút. Chúng tôi đã chạy nhiều hoặc ít hơn vào cùng một vấn đề mà StackOverflow gặp phải: http://samsaffron.com/archive/2011/10/28/in-managed-code-we-trust-our-recent-battles-with-the-net-garbage-collectorThông báo Thu gom Rác Bỏ lỡ

Hiện tại, chúng tôi đang giảm số đối tượng trong bộ nhớ cache. Tuy nhiên, điều này cần có thời gian.

Đồng thời, chúng tôi đã triển khai các phương pháp được ghi thành tài liệu here để nhận thông báo trong .NET về việc tiếp cận các GC. Mục tiêu là đưa máy chủ web ra khỏi trang trại khi GC đang tiếp cận và đưa nó vào trang trại sau khi GC kết thúc. Tuy nhiên, chúng tôi chỉ nhận được thông báo cho 0,7% tổng số GC. Chúng tôi đang sử dụng một maxGenerationThreshold và largeObjectHeapThreshold của 8. Chúng tôi đã thử các ngưỡng khác, nhưng số lượng GC bị mất không thay đổi.

Chúng tôi đang sử dụng bộ sưu tập rác máy chủ đồng thời (http://msdn.microsoft.com/en-us/library/ms229357.aspx). Mã GCLatencyMode tương tác (xem http://msdn.microsoft.com/en-us/library/system.runtime.gclatencymode.aspx). Ở đây một lần nữa, chúng tôi đã cố gắng sử dụng các chế độ GC khác (Chế độ máy trạm, Hàng loạt, v.v.). Và một lần nữa, chúng tôi không nhận được thông báo cho hầu hết các GC.

Chúng tôi có đang làm gì sai hay không thể nhận được thông báo cho mọi GC xảy ra? Làm cách nào để tăng số lượng thông báo?

Theo http://assets.red-gate.com/community/books/assets/Under_the_Hood_of_.NET_Management.pdf, khi bắt đầu GC được kích hoạt khi Gen2 chạm ~ 10 MB. Chúng tôi có rất nhiều RAM, vì vậy nếu chúng tôi có thể đặt ngưỡng này theo cách thủ công ở cấp cao hơn, sẽ mất nhiều thời gian hơn để đạt đến ngưỡng này và theo hiểu biết của tôi, xác suất sẽ tăng lên để nhận thông báo. Có cách nào để sửa đổi ngưỡng này không?

Đây là mã dùng để đăng ký và nghe thông báo:

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

bạn có thể có thể gửi mã nơi bạn đăng ký và nghe thông báo GC? – Alex

+0

'GC.RegisterForFullGCNotification (gcThreshold, gcThreshold); // Bắt đầu chuỗi bằng WaitForFullGCProc. thWaitForFullGC = chủ đề mới (WaitForFullGCProc); thWaitForFullGC.Name = "HealthTestGCNotificationListenerThread (Ngưỡng =" + gcThreshold + ")"; thWaitForFullGC.IsBackground = true; – kopernik

Trả lời

3

Lưu ý: Đây là chi tiết của một bình luận nhưng bao gồm một mẫu mã lớn .

Bạn đã cân nhắc cố gắng nhận thông báo GC của mình theo cách khác? Jeffrey Richter (CLR thông qua C#) giải thích một cách tốt để nhận được thông báo, nó sử dụng một đối tượng và kiểm tra phương pháp finalizer của nó trong thế hệ nó là gì.

Đây là lớp: Nó sử dụng các đối tượng bên trong được thu thập nếu thế hệ được cung cấp phù hợp (xem ví dụ new GenObject(0);) hoặc phục hồi cho thế hệ cao hơn kế tiếp.

Và bạn chỉ cần đăng ký nó với 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

Đây là một ý tưởng hay, nhưng tiếc là nó chỉ thông báo cho tôi sau khi GC kết thúc. Nó không thông báo cho tôi về bất kỳ bộ sưu tập rác sắp tới ... – kopernik