2013-06-19 2 views
0

나는 여러 컴퓨터를 모니터링하고 데이터를 데이터베이스에 저장하며 대시 보드에 여러 개의 차트를 표시하는 응용 프로그램을 작성하고 있습니다.WPF Toolkit Line Chart를 새로 고치는 동안 메모리 누수가 발생합니까?

<chartingToolkit:Chart x:Name="chart" BorderThickness="0" Foreground="Gray"/> 

그럼, 내가 응용 프로그램의 흐름을 차트를 새로 고침하는 System.Timers.Timer를 시작

여기의 WPF UserControl을에 차트를 만들기위한 내 XAML 소스입니다.

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의 프로그래밍 방식으로 추가 : 다음 차트를 새로 고침에 대한 책임 코드입니다.

문제는 몇 분 후이 코드에서 사용되는 메모리가 매우 빠르게 증가하고 있다는 것입니다 (이와 같은 10 개의 차트가 있으며 약 15 분 안에 약 400MB). 댓글에서 볼 수 있듯이 질문과 답변이 너무 많아서 응용 프로그램의 RAM 사용이 늘어나는 것을 막기 위해 여러 가지 작업을 시도했지만 전체 algorythm을 조정하려했지만 성공하지 못했습니다.

현재 수정 방법에 대한 아이디어가 부족합니다. 응용 프로그램은 연중 무휴로 작동해야하며 안정적이어야합니다.

해결책을 찾고있는 며칠 밤이 지나면이 문제로 저를 도울 수 있다면 매우 기쁠 것입니다.

답변

0

당신이 옳은 것처럼 보입니다.
간단한 프로젝트를 작성하여 사례를 에뮬레이션하고 결과가 다시 수정되었습니다.
데이터 양이 합리적 일지라도 메모리 소비는 엄청납니다.

콘크리트 결과 : LinePointsCount = 128, LinesCount = 10, TimerIntervalInMilliseconds = 300 -되지 제한 메모리 소비 LinePointsCount = 128, LinesCount = 10, TimerIntervalInMilliseconds = 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; } 
} 

내가 = "3.5.50211.1"

에 의해을 WPFToolkit.DataVisualization 버전을 사용 그런데 데이터 구조에서 차트를 제거 할 때 차트에서 선을 제거해야 할 수도 있습니다.
어쨌든 문제가 있으며 가능한 해결책은 차트에 제공하는 데이터 양을 줄이고 업데이트 간격을 늘리는 것입니다.

+0

빠른 답장을 보내 주셔서 감사합니다. 나는 당신의 코드 params와 함께 놀려고했지만 여전히 많은 양의 데이터가 소비되고있다. 재미있는 점은 D3 DynamicDataDisplay 차트를 사용하려고했는데 그 효과가 여전히 동일하다는 것입니다. 그리고,'timer_elapsed' 메소드마다'chart.Series.Clear()'를 호출하도록 코드를 수정하면 결과도 동일하게됩니다 - 메모리는 여전히 증가하고 있습니다.필자는 좋은 추억을 가지고 있기 때문에 WinForms ZedGraph에 대해 생각하기 시작했습니다. – greenskin

+0

이상한 것은 간격이 1000ms이면 동작이 재현되지 않는다는 것입니다. 어쩌면 우리가 300ms를 설정한다면 데이터 가져 오기는 300ms 이상 걸리고 디스패처 큐는 끝없이 성장하기 시작합니다 ... 불행히도 지금은 확인하지 않아도됩니다. – FireAlkazar

0

몇 가지 추가 수정을 거친 후 필자는 자체 차트 컨트롤을 만들기로 결정했습니다. 자, 선 그래프를 캔버스에 직접 그리고 메모리 소비가 안정적입니다. 그것은 또한 훨씬 더 빠르다, 나는 덧붙여 야한다;].

@FireAlkazar : 어쨌든, 답장을 보내 주셔서 감사합니다. 건배!