Tôi có một ứng dụng WPF đang gặp phải rất nhiều vấn đề về hiệu năng. Điều tồi tệ nhất là đôi khi ứng dụng chỉ đóng băng trong vài giây trước khi chạy lại.Giám sát Thùng rác trong C#
Tôi hiện đang gỡ lỗi ứng dụng để xem những gì đóng băng này có thể liên quan đến, và tôi tin rằng một trong những điều có thể gây ra nó là Bộ sưu tập rác. Vì ứng dụng của tôi đang chạy trong một môi trường rất hạn chế, tôi tin rằng Garbage Collector có thể sử dụng tất cả các tài nguyên của máy khi nó được chạy và không để lại cho ứng dụng của chúng ta.
Để kiểm tra giả thuyết này, tôi tìm thấy các bài viết sau: Garbage Collection Notifications và Garbage Collection Notifications in .NET 4.0, giải thích cách ứng dụng của tôi có thể được thông báo khi Bộ thu gom rác sẽ bắt đầu chạy và khi nó kết thúc.
Vì vậy, dựa trên những bài báo tôi đã tạo các lớp dưới đây để nhận được thông báo:
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();
}
}
Và tôi đã thêm các tùy chọn gcConcurrent đến file app.config của tôi (dưới đây):
<?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>
Tuy nhiên, bất cứ khi nào ứng dụng được thực hiện, có vẻ như không có thông báo nào được gửi mà Bộ thu gom rác sẽ chạy. Tôi đã đặt breakpoint trong DoGCMonitoring và nó xuất hiện rằng các điều kiện (s == GCNotificationStatus.Succeeded) và (s == GCNotificationStatus.Succeeded) không bao giờ hài lòng, do đó nội dung của các câu lệnh ifs không bao giờ được thực hiện.
Tôi đang làm gì sai?
Lưu ý: Tôi đang sử dụng C# với WPF và Khuôn khổ .NET 3.5.
CẬP NHẬT 1
cập nhật thử nghiệm GCMonitor tôi với phương pháp AllocationTest. Phương pháp này chỉ dành cho mục đích thử nghiệm. Tôi chỉ muốn đảm bảo rằng đủ bộ nhớ đã được cấp phát để buộc Garbage Collector chạy.
UPDATE 2
Cập nhật phương pháp DoGCMonitoring, với kiểm tra mới về sự trở lại của các phương pháp WaitForFullGCApproach và WaitForFullGCComplete. Từ những gì tôi đã thấy cho đến nay ứng dụng của tôi sẽ trực tiếp đến điều kiện (s == GCNotificationStatus.NotApplicable). Vì vậy, tôi nghĩ rằng tôi có một số cấu hình sai ở đâu đó ngăn tôi lấy kết quả mong muốn.
Tài liệu cho GCNotificationStatus enum có thể được tìm thấy here.
Bạn đã thử thực sự lược tả nó với một công cụ, nói điều gì đó giống như màn hình hiệu suất cửa sổ hoặc windbg - thay vì cố gắng để viết một wrapper GC? –
Có thể GC chưa chạy (chưa). Bạn có thể hiển thị AllocationTest() không? –
Xin chào, tôi thực sự có một công cụ định hình, tuy nhiên vấn đề đóng băng mà tôi đã đề cập trước đó đang xảy ra trong môi trường sản xuất chứ không phải trên máy của tôi (tôi không thể tái tạo nó). Và thật không may cho tôi, tôi không thể chạy công cụ lược tả trong môi trường sản xuất. – Felipe