2013-06-19 17 views
0

我正在編寫應用程序,它將監視多臺計算機,將數據存儲在數據庫中,並將其顯示在儀表板上,每隔幾秒刷新一次。刷新WPF Toolkit折線圖時內存泄漏?

這裏是我的WPF的用戶控件創建圖表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)。 正如你可以在評論中看到的那樣,在所發現的問題和答案的引導下,我試圖做一些事情來防止我的應用程序的RAM使用增長,我也嘗試調整整個algorythm,但沒有成功。

目前我正在用盡想法如何解決它。應用程序應該24/7工作,它必須是穩定的。

經過一段時間尋找解決方案,我會很高興,如果你能幫我解決這個問題。

回答

0

似乎你是對的。
我寫了一個簡單的項目,模擬你的情況,你的結果被重寫。
即使一些數據似乎是合理的,內存消耗也是巨大的。

具體結果: LinePointsCount = 128,LinesCount = 10,TimerIntervalInMilliseconds = 300 - 不限內存消耗 LinePointsCount = 128,LinesCount = 10,TimerIntervalInMilliseconds = 1000 - 存儲器犯規增加140MB

發佈我的代碼在如果你想使用參數玩:

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; } 
} 

我用WPFToolkit.DataVisualization版本= 「3.5.50211.1」

而且通過順便說一句,也許你還需要從圖表中刪除行,當你從數據結構中刪除它們。
因此無論如何問題存在,可能的解決方案是減少您爲圖表提供的數據量並增加更新間隔。

+0

感謝您的快速回復。我試圖玩你的代碼的參數,仍然有大量的數據消耗。有趣的是,我試圖使用D3 DynamicDataDisplay圖表,效果仍然相同。而且,當我修改我的代碼以在每個'timer_elapsed'方法中調用'chart.Series.Clear()'時,結果也是一樣的 - 內存仍在增長。我開始考慮將WinForms ZedGraph加入其中,因爲我對它有着美好的回憶。 – greenskin

+0

奇怪的是,如果間隔爲1000毫秒,則不會再現行爲。也許如果我們設置300ms的數據獲取時間超過300ms,調度器隊列開始無限增長......可惜現在沒有時間去檢查它了 – FireAlkazar

0

經過一些額外的修改後,我決定創建我自己的圖表控件。現在,我自己在畫布上繪製折線圖,​​內存消耗也很穩定。它也快得多,我必須補充;]。

@FireAlkazar:無論如何,感謝您的回覆。乾杯!