У меня есть приложение WPF, которое испытывает много проблем с производительностью. Хуже всего то, что иногда приложение просто останавливается на несколько секунд, а затем снова запускается.
В настоящее время я отлаживаю приложение, чтобы выяснить, с чем может быть связано это замораживание, и я полагаю, что одной из причин, вызывающих его, является сборщик мусора. Поскольку мое приложение работает в очень ограниченной среде, я считаю, что сборщик мусора может использовать все ресурсы машины при запуске и не оставлять ни одного нашему приложению.
Чтобы проверить эту гипотезу, я нашел следующие статьи: Уведомления о сборке мусора и Уведомления о сборке мусора в .NET 4.0 , в которых объясняется, как мое приложение может получать уведомления о запуске сборщика мусора работает и когда он закончен.
Итак, на основе этих статей я создал класс ниже для получения уведомлений:
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();
}
}
И я добавил опцию gcConcurrent в мой файл app.config (ниже):
<?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>
Однако при каждом запуске приложения создается впечатление, что не отправляется уведомление о том, что сборщик мусора будет запущен. Я установил точки останова в DoGCMonitoring, и кажется, что условия (s == GCNotificationStatus.Succeeded) и (s == GCNotificationStatus.Succeeded) никогда не выполняются, поэтому содержимое этих операторов if никогда не выполняется.
Что я делаю не так?
Примечание. Я использую C # с WPF и .NET Framework 3.5.
ОБНОВЛЕНИЕ 1
Обновлен мой тест GCMonitor с помощью метода AllocationTest. Этот метод предназначен только для тестирования. Я просто хотел убедиться, что выделяется достаточно памяти для запуска сборщика мусора.
ОБНОВЛЕНИЕ 2
Обновлен метод DoGCMonitoring, добавлены новые проверки возврата методов WaitForFullGCApproach и WaitForFullGCComplete. Из того, что я видел до сих пор, мое приложение переходит непосредственно в состояние (s == GCNotificationStatus.NotApplicable). Так что я думаю, что у меня где-то есть неправильная конфигурация, которая мешает мне получить желаемые результаты.
Документацию для перечисления GCNotificationStatus можно найти здесь .