C# 刷新WPF工具包折线图时内存泄漏?

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

我正在编写一个应用程序,它将监视多台计算机,将数据存储在数据库中,并在仪表板上显示数据,每隔几秒钟就会刷新多个图表

以下是我在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);
}
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菜单项单击(对象)