複数のコンピューターを監視し、データをデータベースに保存し、ダッシュボードに表示して、数秒ごとに複数のグラフを更新するアプリケーションを作成しています。
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 がプログラムで追加されました。
問題は、このコードによって使用されるメモリが数分後に非常に急速に増加することです (このようなグラフが 10 個ある場合、15 分間で約 400 MB になります)。コメントでわかるように、SO で見つかった質問と回答に導かれて、アプリケーションの RAM 使用量の増加を防ぐためにいくつかのことを試みました。また、アルゴリズム全体を調整しようとしましたが、成功しませんでした。
現在、私はそれを修正する方法のアイデアが不足しています。アプリケーションは 24 時間年中無休で動作し、安定している必要があります。
何日も何日も解決策を探した後、この問題について助けていただければ幸いです。