多くのパフォーマンスの問題が発生しているWPFアプリケーションがあります。それらの最悪の場合は、アプリケーションが数秒間フリーズしてから、再び実行されることがあります。Cでのガベージコレクタの監視
私は現在、このフリーズが関連している可能性があるかどうかを確認するためにアプリケーションをデバッグしています。その原因の1つはガベージコレクタです。私のアプリケーションは非常に限られた環境で動作しているので、ガベージコレクタは実行時にすべてのマシンのリソースを使用し、アプリケーションには何も残さないと考えています。
この仮説を確認するには、ガベージコレクタの実行開始時と終了時にアプリケーションにどのように通知するかを説明する記事、Garbage Collection NotificationsとGarbage Collection Notifications in .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();
}
}
そして私は私のapp.configファイル(下記)にgcConcurrentオプションを追加しました:
<?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)は決して満たされないため、これらのifsステートメントの内容は決して実行されません。
私は間違っていますか?
注:私はWPFと.NET Framework 3.5でC#を使用しています。
UPDATE 1
はAllocationTest方法で私のGCMonitorテストを更新しました。このメソッドは、テスト目的のみです。ガベージコレクタを強制的に実行するのに十分なメモリが割り当てられていることを確認したかっただけです。方法WaitForFullGCApproachとWaitForFullGCCompleteの復帰に新しい小切手と、DoGCMonitoring方法を更新しました
UPDATE 2
。これまで私が見たことから、私のアプリケーションは直接(s == GCNotificationStatus.NotApplicable)の状態になります。だから私はどこかに誤った設定があると思って、望みの結果が得られなくなってしまった。
GCNotificationStatus列挙型のドキュメントはhereです。
実際にツールを使ってプロファイリングしようとしましたか、Windowsパフォーマンスモニタやwindbgのようなものですが、GCラッパーを作成するのではなく、 –
GCが実行されていない可能性があります(まだ)。 AllocationTest()を表示できますか? –
こんにちは、私は実際にプロファイリングツールを持っていますが、私が前に述べたフリーズの問題は、私のマシンではなく(私はそれを再現できません)、実稼働環境で起こっています。そして残念ながら、私は生産環境でプロファイリングツールを実行できません。 – Felipe