我正在编写应用程序,它将监视多台计算机,将数据存储在数据库中并将其显示在仪表板上,并且每隔几秒钟刷新多个图表。
这是我在 wpf UserControl 上创建图表的 xaml 源代码:
<chartingToolkit:Chart x:Name="chart" BorderThickness="0" Foreground="Gray"/>
然后,我启动一个 System.Timers.Timer 来刷新应用程序流图表。这是负责刷新图表的代码片段:
private Dictionary<string, List<RamPlot>> data = new Dictionary<string, List<RamPlot>>();
void refreshChartTimer_Elapsed(object sender, ElapsedEventArgs e)
{
DateTime now = DateTime.Now;
lock (lockObject)
{
//Getting info about hosts from my storage
List<HostInfo> infos = LiveHostInfoManager.GetInstance().GetHostInfos();
this.Dispatcher.Invoke(new Action(() =>
{
foreach (HostInfo info in infos)
{
//data contains info about host, so I add new values to existing one, creating data for linechart
if (data.ContainsKey(info.HostName))
{
data[info.HostName].Add(new RamPlot(DateTime.Now, (info.RamInfo.TotalSize - info.RamInfo.CurrentlyAvailable) / info.RamInfo.TotalSize));
//I want to display on chart only last 20 readings
if (data[info.HostName].Count > 20)
{
data[info.HostName].RemoveAt(0);
}
}
else
{
//If the host is not in my dictionary (connected before last iteration was performed), I add it to my dictionary
if (info.RamInfo != null)
{
List<RamPlot> plot = new List<RamPlot>();
//Thought, that it can be due to List's load factor, hence I set up capacity. Apparently - not.
plot.Capacity = 25;
plot.Add(new RamPlot(DateTime.Now, (info.RamInfo.TotalSize - info.RamInfo.CurrentlyAvailable) / info.RamInfo.TotalSize));
data.Add(info.HostName, plot);
}
}
}
//for hosts that are no longer available, I perform cleanup to get rid of them from my linechart
List<string> keysToDelete = new List<string>();
foreach (KeyValuePair<string, List<RamPlot>> kvp in data)
{
bool exists = false;
foreach (HostInfo info in infos)
{
if (info.HostName.Equals(kvp.Key))
{
exists = true;
break;
}
}
if (!exists)
{
keysToDelete.Add(kvp.Key);
}
}
foreach (string key in keysToDelete)
{
data.Remove(key);
}
//Here I attach my data to line chart. If I comment this block, I detect no memory leaks
foreach (KeyValuePair<string, List<RamPlot>> kvp in data)
{
bool exists = false;
foreach (LineSeries series in chart.Series)
{
if (series.Title.ToString().Equals(kvp.Key) && !string.IsNullOrEmpty(kvp.Key))
{
series.ItemsSource = null;
series.ItemsSource = kvp.Value;
exists = true;
break;
}
}
if (!exists && !string.IsNullOrEmpty(kvp.Key))
{
LineSeries series = new LineSeries();
series.Title = kvp.Key;
series.IndependentValueBinding = new Binding("Date");
series.DependentValueBinding = new Binding("Usage");
series.ItemsSource = kvp.Value;
chart.Series.Add(series);
}
}
}));
//Thought that if I recreate all data structure, some garbage might be cleaned up by GC. Apparently - not.
data = new Dictionary<string, List<RamPlot>>(data);
}
}
我不知道启动时连接到我的应用程序的主机数量,因此 LineSeries 以编程方式添加。
问题是,几分钟后,这段代码使用的内存增长得非常快(有十个这样的图表,15 分钟内大约 400 MB)。正如您在评论中看到的那样,由 SO 上的问题和答案引导,我尝试做几件事来防止我的应用程序的 RAM 使用量增长,我还尝试调整整个算法,但没有成功。
目前我已经没有办法解决它了。应用程序应该 24/7 全天候工作并且它必须是稳定的。
经过几天几夜的寻找解决方案,如果你能帮助我解决这个问题,我会很高兴。