C# 刷新WPF工具包折线图时内存泄漏?
我正在编写一个应用程序,它将监视多台计算机,将数据存储在数据库中,并在仪表板上显示数据,每隔几秒钟就会刷新多个图表 以下是我在wpf UserControl上创建图表的xaml源代码:C# 刷新WPF工具包折线图时内存泄漏?,c#,memory-leaks,wpftoolkit,C#,Memory Leaks,Wpftoolkit,我正在编写一个应用程序,它将监视多台计算机,将数据存储在数据库中,并在仪表板上显示数据,每隔几秒钟就会刷新多个图表 以下是我在wpf UserControl上创建图表的xaml源代码: <chartingToolkit:Chart x:Name="chart" BorderThickness="0" Foreground="Gray"/> 然后,我启动System.Timers.Timer来刷新应用程序流上的图表。以下是负责刷新图表的代码段: private Dicti
<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);
}
private Dictionary data=new Dictionary();
无效刷新图表计时器已过(对象发送器,ElapsedEventArgs e)
{
DateTime now=DateTime.now;
锁定(锁定对象)
{
//从我的存储中获取有关主机的信息
List infos=LiveHostInfoManager.GetInstance().GetHostInfos();
this.Dispatcher.Invoke(新操作(()=>
{
foreach(信息系统中的主机信息)
{
//数据包含有关主机的信息,所以我向现有值添加新值,为折线图创建数据
if(data.ContainsKey(info.HostName))
{
数据[info.HostName].Add(新RamPlot(DateTime.Now,(info.RamInfo.TotalSize-info.RamInfo.CurrentlyAvailable)/info.RamInfo.TotalSize);
//我只想在图表上显示最后20个读数
如果(数据[info.HostName]。计数>20)
{
数据[info.HostName].RemoveAt(0);
}
}
其他的
{
//如果主机不在我的字典中(在执行上一次迭代之前已连接),我会将其添加到字典中
如果(info.RamInfo!=null)
{
列表打印=新列表();
//我想,这可能是由于列表的负载因素,所以我设置了容量。显然不是。
容积=25;
plot.Add(新RamPlot(DateTime.Now,(info.RamInfo.TotalSize-info.RamInfo.currentlayavailable)/info.RamInfo.TotalSize);
添加(info.HostName,plot);
}
}
}
//对于不再可用的主机,我将执行清理以从折线图中删除它们
List keystedelete=新列表();
foreach(数据中的KeyValuePair kvp)
{
bool exists=false;
foreach(信息系统中的主机信息)
{
if(info.HostName.Equals(kvp.Key))
{
存在=真;
打破
}
}
如果(!存在)
{
keystedelete.Add(kvp.Key);
}
}
foreach(keystedelete中的字符串键)
{
数据删除(键);
}
//在这里,我将数据附加到折线图上。如果我对该块进行注释,则不会检测到内存泄漏
foreach(数据中的KeyValuePair kvp)
{
bool exists=false;
foreach(图表中的线系列。系列)
{
if(series.Title.ToString().Equals(kvp.Key)和&!string.IsNullOrEmpty(kvp.Key))
{
series.ItemsSource=null;
series.ItemsSource=kvp.Value;
存在=真;
打破
}
}
如果(!exists&&!string.IsNullOrEmpty(kvp.Key))
{
LineSeries=新的LineSeries();
series.Title=kvp.Key;
series.IndependentValueBinding=新绑定(“日期”);
series.DependentValueBinding=新绑定(“用法”);
series.ItemsSource=kvp.Value;
图表.系列.添加(系列);
}
}
}));
//我想如果我重新创建所有的数据结构,一些垃圾可能会被GC清除。显然不是。
数据=新字典(数据);
}
}
我不知道启动时连接到我的应用程序的主机数量,因此以编程方式添加了LineSeries
问题是,几分钟后,这段代码使用的内存增长非常快(有十个像这样的图表,15分钟内大约400 MB)。
正如您在评论中看到的,在SO上找到的问题和答案的引导下,我尝试做了一些事情来防止我的应用程序的RAM使用量增长,我还尝试调整整个算法,但没有成功
目前我没有办法解决这个问题。应用程序应该全天候工作,并且必须稳定
在日日夜夜寻找解决方案之后,如果您能帮助我解决这个问题,我将非常高兴。似乎您是对的。
我编写了一个简单的项目,模拟您的案例,然后重新介绍您的结果。
即使数据量看起来是合理的,内存消耗也是巨大的 具体成果: LinePointsCount=128,LinesCount=10,TimerIntervalinMissicles=300-不限制内存消耗 linepointscont=128,linescont=10,timerintervalinmissions=1000-内存不会增加140Mb 如果您想玩params,我会发布代码:
public partial class MainWindow : Window
{
const int LinePointsCount = 128;
const int LinesCount = 20;
const int TimerIntervalInMilliseconds = 1000;
private static DateTime Current = DateTime.Now;
Random _random = new Random();
List<string> _chartNames;
public MainWindow()
{
InitializeComponent();
_chartNames = Enumerable.Repeat(1, LinesCount).Select((con, index) => index.ToString()).ToList();
}
Timer _timer;
private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
{
_timer = new Timer((o) => Dispatcher.Invoke(new Action(ShowData)));
_timer.Change(0, TimerIntervalInMilliseconds);
}
private void MenuItem_OnClick(object sender, RoutedEventArgs e)
{
}
private void ShowData()
{
var data = GetData();
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);
}
}
}
Dictionary<string, List<RamPlot>> GetData()
{
var result = new Dictionary<string, List<RamPlot>>();
var chartName = GetRandomChartName();
result.Add(chartName, new List<RamPlot>
{
new RamPlot{Date = Current, Usage = 100},
new RamPlot{Date = Current.AddDays(-LinePointsCount), Usage = 300},
});
var random = _random.Next(101, 300);
for (int i = 1; i < LinePointsCount; i++)
{
var newElement = new RamPlot { Date = Current.AddDays(-i), Usage = random };
result[chartName].Add(newElement);
}
return result;
}
string GetRandomChartName()
{
var nextIndex = _random.Next(0, _chartNames.Count);
return _chartNames[nextIndex];
}
}
public class RamPlot
{
public DateTime Date { get; set; }
public int Usage { get; set; }
}
公共部分类主窗口:窗口
{
常量int linepointscont=128;
常数int LINESCONT=20;
const int timerintervalinmissions=1000;
private static DateTime Current=DateTime.Now;
随机_Random=新随机();
列出图表名称;
公共主窗口()
{
初始化组件();
_chartNames=可枚举。重复(1,lineScont)。选择((con,index)=>index.ToString()).ToList();
}
定时器(u定时器),;
私有void主窗口\u已加载(对象发送方,路由目标)
{
_计时器=新计时器((o)=>Dispatcher.Invoke(新操作(ShowData));
_timer.Change(0,timerintervalin毫秒);
}
私有void菜单项单击(对象)