Surveillance Garbage Collector en C#

J'ai une application WPF qui rencontre beaucoup de problèmes de performances. Le pire d'entre eux est que, parfois, l'application se fige pendant quelques secondes avant de la remettre en marche.

Je suis actuellement en train de débogage de l'application pour voir ce que ce gel peut être lié à, et je crois que l'une des choses qui peuvent être à l'origine c'est le Garbage Collector. Depuis mon application est en cours d'exécution dans une mesure très limitée de l'environnement, je crois que le Garbage Collector peut être en utilisant toutes les ressources de la machine quand il est exécuté et laissant aucun, à notre application.

À les vérifier des hypothèses, j'ai trouvé ces articles: La Collecte Des Ordures Notifications et La Collecte des ordures Notifications .NET 4.0, qui expliquent comment faire ma demande peut être notifié lorsque le Garbage Collector va commencer à courir et quand c'est fini.

Sur la base de ces articles, j'ai créé la catégorie ci-dessous pour obtenir les notifications:

public sealed class GCMonitor
{
private static volatile GCMonitor instance;
private static object syncRoot = new object();
private Thread gcMonitorThread;
private ThreadStart gcMonitorThreadStart;
private bool isRunning;
public static GCMonitor GetInstance()
{
if (instance == null)
{
lock (syncRoot)
{
instance = new GCMonitor();
}
}
return instance;
}
private GCMonitor()
{
isRunning = false;
gcMonitorThreadStart = new ThreadStart(DoGCMonitoring);
gcMonitorThread = new Thread(gcMonitorThreadStart);
}
public void StartGCMonitoring()
{
if (!isRunning)
{
gcMonitorThread.Start();
isRunning = true;
AllocationTest();
}
}
private void DoGCMonitoring()
{
long beforeGC = 0;
long afterGC = 0;
try
{
while (true)
{
//Check for a notification of an approaching collection.
GCNotificationStatus s = GC.WaitForFullGCApproach(10000);
if (s == GCNotificationStatus.Succeeded)
{
//Call event
beforeGC = GC.GetTotalMemory(false);
LogHelper.Log.InfoFormat("===> GC <=== " + Environment.NewLine + "GC is about to begin. Memory before GC: %d", beforeGC);
GC.Collect();
}
else if (s == GCNotificationStatus.Canceled)
{
LogHelper.Log.Info("===> GC <=== " + Environment.NewLine + "GC about to begin event was cancelled");
}
else if (s == GCNotificationStatus.Timeout)
{
LogHelper.Log.Info("===> GC <=== " + Environment.NewLine + "GC about to begin event was timeout");
}
else if (s == GCNotificationStatus.NotApplicable)
{
LogHelper.Log.Info("===> GC <=== " + Environment.NewLine + "GC about to begin event was not applicable");
}
else if (s == GCNotificationStatus.Failed)
{
LogHelper.Log.Info("===> GC <=== " + Environment.NewLine + "GC about to begin event failed");
}
//Check for a notification of a completed collection.
s = GC.WaitForFullGCComplete(10000);
if (s == GCNotificationStatus.Succeeded)
{
//Call event
afterGC = GC.GetTotalMemory(false);
LogHelper.Log.InfoFormat("===> GC <=== " + Environment.NewLine + "GC has ended. Memory after GC: %d", afterGC);
long diff = beforeGC - afterGC;
if (diff > 0)
{
LogHelper.Log.InfoFormat("===> GC <=== " + Environment.NewLine + "Collected memory: %d", diff);
}
}
else if (s == GCNotificationStatus.Canceled)
{
LogHelper.Log.Info("===> GC <=== " + Environment.NewLine + "GC finished event was cancelled");
}
else if (s == GCNotificationStatus.Timeout)
{
LogHelper.Log.Info("===> GC <=== " + Environment.NewLine + "GC finished event was timeout");
}
else if (s == GCNotificationStatus.NotApplicable)
{
LogHelper.Log.Info("===> GC <=== " + Environment.NewLine + "GC finished event was not applicable");
}
else if (s == GCNotificationStatus.Failed)
{
LogHelper.Log.Info("===> GC <=== " + Environment.NewLine + "GC finished event failed");
}
Thread.Sleep(1500);
}
}
catch (Exception e)
{
LogHelper.Log.Error("  ********************   Garbage Collector Error  ************************ ");
LogHelper.LogAllErrorExceptions(e);
LogHelper.Log.Error("  -------------------   Garbage Collector Error  --------------------- ");
}
}
private void AllocationTest()
{
//Start a thread using WaitForFullGCProc.
Thread stress = new Thread(() =>
{
while (true)
{
List<char[]> lst = new List<char[]>();
try
{
for (int i = 0; i <= 30; i++)
{
char[] bbb = new char[900000]; //creates a block of 1000 characters
lst.Add(bbb);                //Adding to list ensures that the object doesnt gets out of scope
}
Thread.Sleep(1000);
}
catch (Exception ex)
{
LogHelper.Log.Error("  ********************   Garbage Collector Error  ************************ ");
LogHelper.LogAllErrorExceptions(e);
LogHelper.Log.Error("  -------------------   Garbage Collector Error  --------------------- ");
}
}
});
stress.Start();
}
}

Et j'ai ajouté le gcConcurrent option pour mon application.fichier de configuration (ci-dessous):

<?xml version="1.0"?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net-net-2.0"/>
</configSections>
<runtime>
<gcConcurrent enabled="false" />
</runtime>
<log4net>
<appender name="Root.ALL" type="log4net.Appender.RollingFileAppender">
<param name="File" value="../Logs/Root.All.log"/>
<param name="AppendToFile" value="true"/>
<param name="MaxSizeRollBackups" value="10"/>
<param name="MaximumFileSize" value="8388608"/>
<param name="RollingStyle" value="Size"/>
<param name="StaticLogFileName" value="true"/>
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%date [%thread] %-5level - %message%newline"/>
</layout>
</appender>
<root>
<level value="ALL"/>
<appender-ref ref="Root.ALL"/>
</root>
</log4net>
<appSettings>
<add key="setting1" value="1"/>
<add key="setting2" value="2"/>
</appSettings>
<startup>
<supportedRuntime version="v2.0.50727"/>
</startup>
</configuration>

Cependant, chaque fois que l'application est exécutée, il me semble que si aucune notification n'est envoyée que le Garbage Collector sera exécuté. J'ai mis des points d'arrêt dans le DoGCMonitoring et il apparaît que les conditions (s == GCNotificationStatus.Réussi) et (s == GCNotificationStatus.Réussi) ne sont jamais satisfaits, par conséquent, le contenu de ceux-fi états ne sont jamais exécutées.

Ce que je fais mal?

Note: je suis à l'aide de C# avec WPF et de l' .NET Framework 3.5.

Mise à JOUR de 1

Mis à jour mon GCMonitor test avec le AllocationTest méthode. Cette méthode est utilisée pour des fins de test uniquement. Je voulais juste vous assurer que suffisamment de mémoire a été allouée pour forcer le Garbage Collector à s'exécuter.

Mise à JOUR 2

Mis à jour le DoGCMonitoring méthode, avec de nouvelles vérifications sur le retour de l'méthodes WaitForFullGCApproach et WaitForFullGCComplete. De ce que j'ai vu jusqu'à présent, mon application qui va directement à l' (s == GCNotificationStatus.NotApplicable) condition. Donc, je pense que j'ai quelques problème de configuration quelque part qui m'en empêche d'obtenir les résultats souhaités.

La documentation de la GCNotificationStatus enum peut être trouvé ici.

  • Avez-vous essayé en fait de profilage avec un outil, dire quelque chose comme l'analyseur de performances de windows ou windbg au lieu d'essayer d'écrire un GC wrapper?
  • Peut-être que la GC n'est pas en cours d'exécution (encore). Pouvez-vous montrer AllocationTest() ?
  • Salut, en fait, je dois avoir un outil de profilage, cependant le gel problème que j'ai mentionné plus tôt qui se passe dans un environnement de production, et non pas sur ma machine (je ne peux pas le reproduire). Et malheureusement pour moi je ne peut pas exécuter l'outil de profilage dans la production de l'environnement.
  • Si vous pensez que c'est la GC, vous pouvez essayer de créer une machine virtuelle avec très peu de RAM (dire, WinXP avec 256 mo de mémoire) et l'exécution de votre demande. Si le problème réapparaît, augmenter la quantité de RAM disponible et essayez à nouveau. Aussi, il est normal que le GC de congeler vos processus pendant qu'il fait sa chose. Après tout, il a besoin d'un état figé affaires dans le but de parcourir le graphe d'objets et de trouver des éléments admissibles pour la collecte.
  • Quelles preuves avez-vous que GC est le problème?
  • c'est techniquement incorrect. Le serveur GC (utilisé dans ASP.NET par exemple) est un cessez-le-monde garbage collector. La station de travail GC (utilisé dans WPF, et la plupart des autres applications de bureau) est une concurrente GC - il n'arrête pas le monde.
  • Oh... je ne savais pas... je me demande comment il peut faire sa chose si l'objet graphique ne cesse de changer de dessous?
  • L' .NET simultanées GC, comme la JVM simultanées GC, s'arrête toujours du monde pour la première marque. Autant que je sache, la seule x86 sans un tas proportionnelle arrêter le monde est Azul Zing. Vous pouvez lire un peu plus à propos de " stop-le-monde en simultané JVM ici... blog.griddynamics.com/2011/06/...

InformationsquelleAutor Felipe | 2012-03-12