2013-02-21 18 views
5

Prowadzimy farmę internetową przy użyciu .NET. Każdy serwer WWW zawiera znaczną ilość statycznych obiektów w swojej pamięci. Odśmiecanie Gen 2 (GC) trwa 10-20 sekund i trwa co 5 minut. Przeszliśmy mniej więcej w tych samych problemach, które napotkał StackOverflow: http://samsaffron.com/archive/2011/10/28/in-managed-code-we-trust-our-recent-battles-with-the-net-garbage-collectorNieodebrane powiadomienia o zbiorze śmieci

W tej chwili zmniejszamy liczbę obiektów w pamięci podręcznej. Jednak wymaga to czasu.

W tym samym czasie wdrożyliśmy metody udokumentowane jako here, aby otrzymywać powiadomienia w .NET o zbliżaniu się do GC. Celem jest wyjęcie serwera z farmy, gdy zbliża się GC i włączenie go do farmy po zakończeniu GC. Otrzymujemy jednak tylko powiadomienie o 0,7% wszystkich GC. Używamy maxGenerationThreshold i largeObjectHeapThreshold z 8. Próbowaliśmy innych progów, ale ilość nieodebranych GC nie uległa zmianie.

Używamy zbierania śmieci z serwera współbieżnego (http://msdn.microsoft.com/en-us/library/ms229357.aspx). GCLatencyMode jest interaktywny (patrz http://msdn.microsoft.com/en-us/library/system.runtime.gclatencymode.aspx). Ponownie spróbowaliśmy użyć innych trybów GC (tryb stacji roboczej, wsad itd.). I znowu nie otrzymaliśmy powiadomienia dla większości GC.

Czy robimy coś złego, czy też niemożliwe jest otrzymanie powiadomienia o każdym GC, który ma miejsce? Jak zwiększyć liczbę powiadomień?

Zgodnie z http://assets.red-gate.com/community/books/assets/Under_the_Hood_of_.NET_Management.pdf, na początku wywoływane jest GC, gdy Gen2 trafi ~ 10 MB. Mamy dużo pamięci RAM, więc jeśli moglibyśmy ustawić ten próg ręcznie na wyższy poziom, osiągnięcie tego progu zajęłoby więcej czasu iw moim rozumieniu prawdopodobieństwo wzrosłoby, aby otrzymać powiadomienie. Czy istnieje sposób na modyfikację tego progu?

Jest to kod, który rejestruje i słucha zgłoszeń:

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

prawdopodobnie można pisać kod, gdzie się zarejestrować i słuchać powiadomień GC? – Alex

+0

'GC.RegisterForFullGCNotification (gcThreshold, gcThreshold); // Rozpocznij wątek za pomocą WaitForFullGCProc. thWaitForFullGC = new Thread (WaitForFullGCProc); thWaitForFullGC.Name = "HealthTestGCNotificationListenerThread (Threshold =" + gcThreshold + ")"; thWaitForFullGC.IsBackground = true; – kopernik

Odpowiedz

3

Uwaga: To jest bardziej komentarzu ale zawiera dużą próbkę kodu .

Czy rozważałeś próbę uzyskania swoich powiadomień GC w inny sposób? Jeffrey Richter (CLR za pośrednictwem C#) wyjaśnia dobry sposób na otrzymywanie powiadomień, używa obiektu i sprawdza jego metodę finalizatora w tym, czym jest.

Jest to klasa: używa wewnętrznych obiektów, które są zbierane, jeśli dostarczone pokolenie się zgadza (patrz np. new GenObject(0);) lub wskrzeszone dla następnej wyższej generacji.

I po prostu zapisać się do niego z 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

To fajny pomysł, ale niestety to tylko informuje mnie po zakończeniu GC. Nie informuje mnie o nadchodzących zbiorach śmieci ... – kopernik

Powiązane problemy