前提・実現したいこと
UWPにおいて、折れ線グラフを表示したいです。
グラフを表示するために、外部ライブラリの、「WinRT Xaml Toolkit Data Visualization Controls.UWP」をNugetからインストールして使用しています。
発生している問題・エラーメッセージ
メモリリークと思われる現象が発生しており、折れ線グラフを更新するたびに、使用メモリが増えていきます。また、描画までにかかる処理時間もだんだんと遅くなっていきます(15回グラフ更新すると、1,2秒遅くなりました)。
メモリリークしていることも謎ですが、描画時間が遅くなることのほうが疑問に感じています。
詳しくは、最下部の「追記」項目をお読みください。「該当のソースコード」項目は読み飛ばしていただいてもかまいません。
該当のソースコード
こちらがソースコードです。このアプリは、ボタンを押下すると、500点からなる折れ線グラフを描画・更新します。描画する500点は起動時に乱数によって決定します。
Xaml
1<Page 2 x:Class="ChartTest.MainPage" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:local="using:ChartTest" 6 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 7 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 8 xmlns:Charting="using:WinRTXamlToolkit.Controls.DataVisualization.Charting" 9 mc:Ignorable="d"> 10 11 <Grid x:Name="Grid" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> 12 13 <Charting:Chart Name="LineChart01" Margin="33,154,49,34"> 14 </Charting:Chart> 15 <Button Content="Button" HorizontalAlignment="Left" Height="114" Margin="417,21,0,0" VerticalAlignment="Top" Width="496" Click="Button_Click"/> 16 17 </Grid> 18</Page>
C#
1using WinRTXamlToolkit.Controls.DataVisualization.Charting; 2 3 4namespace ChartTest 5{ 6 public class Item 7 { 8 public double Key { get; set; } 9 public double Value { get; set; } 10 } 11 12 public sealed partial class MainPage : Page 13 { 14 15 List<Item> items; 16 17 public MainPage() 18 { 19 this.InitializeComponent(); 20 21 // 描画する500点を決定し、itemsに代入 22 items = new List<Item>(); 23 Random rnd = new System.Random(); 24 for (int i=0; i<500; i++) 25 { 26 items.Add(new Item { Key=rnd.Next(0, 100), Value=rnd.Next(0, 100) }); 27 } 28 } 29 30 private void Button_Click(object sender, RoutedEventArgs e) 31 { 32 // SeriesのCollectionを初期化 33 this.LineChart01.Series.Clear(); 34 35 // Collection 生成 36 LineSeries lineSeries = new LineSeries() 37 { 38 ItemsSource = items, 39 IndependentValuePath = "Key", 40 DependentValuePath = "Value", 41 Title = "ChartTest" 42 }; 43 44 // SeriesにAdd 45 this.LineChart01.Series.Add(lineSeries); 46 47 } 48 } 49} 50
試したこと
ボタンが押下された際に、this.LineChart01.Series.Clear();
を行って、Collectionの中身をクリアしているにも関わらず、メモリが増えていきます。
そのため、this.Grid.Children.Remove()
を使用してLineChart01
自体を削除することも試みましたが、メモリが減ることはありませんでした。
補足情報(FW/ツールのバージョンなど)
開発環境は、Visual Studio 2017 Communityです。
Debug版での実行に基づき、メモリや処理速度を判断しています。
追記
メモリリークする、処理が遅くなる、についての詳細な部分について記載します。
まず、検証するソースコードですが、今後の実用性の観点から以下のようなアプリに変更しました。
アプリ概要: 「グラフ生成」と「グラフクリア」の二つのボタンが配置されている。「グラフ生成」ボタンを押下すると、10点分の折れ線グラフが30個描画される。次に、「グラフクリア」ボタンを押下すると、描画されているグラフを消す。
ソースコード
Xaml
1<Page 2 x:Class="ChartTest.MainPage" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:local="using:ChartTest" 6 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 7 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 8 xmlns:Charting="using:WinRTXamlToolkit.Controls.DataVisualization.Charting" 9 mc:Ignorable="d"> 10 11 <Grid x:Name="Grid" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> 12 13 <Charting:Chart Name="LineChart01" Margin="33,154,49,34"> 14 </Charting:Chart> 15 <Button Content="グラフ生成" HorizontalAlignment="Left" Height="114" Margin="108,21,0,0" VerticalAlignment="Top" Width="496" Click="Button_Create_Click"/> 16 <Button Content="グラフクリア" HorizontalAlignment="Left" Height="114" Margin="749,21,0,0" VerticalAlignment="Top" Width="496" Click="Button_Clear_Click"/> 17 18 </Grid> 19</Page>
C#
1using WinRTXamlToolkit.Controls.DataVisualization.Charting; 2 3 4namespace ChartTest 5{ 6 public class Item : INotifyPropertyChanged 7 { 8 public event PropertyChangedEventHandler PropertyChanged; 9 private void RaisePropertyChanged([CallerMemberName]string propertyName = null) 10 { 11 if (PropertyChanged != null) 12 PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 13 } 14 15 private double x; 16 private double y; 17 18 public double Key { 19 get { return x; } 20 set { 21 if (value != x) 22 { 23 x = value; 24 RaisePropertyChanged(); 25 } 26 } 27 } 28 public double Value 29 { 30 get { return y; } 31 set 32 { 33 if (value != y) 34 { 35 y = value; 36 RaisePropertyChanged(); 37 } 38 } 39 } 40 41 } 42 43 public sealed partial class MainPage : Page 44 { 45 46 List<ObservableCollection<Item>> itemsList; 47 48 public MainPage() 49 { 50 this.InitializeComponent(); 51 52 // 描画するグラフのList生成 53 itemsList = new List<ObservableCollection<Item>>(); 54 Random rnd = new System.Random(); 55 // 30個のグラフを表示 56 for (int j = 0; j < 30; j++) 57 { 58 // それぞれのグラフに10点の値を入れる。 59 ObservableCollection<Item> items = new ObservableCollection<Item>(); 60 for (int i = 0; i < 10; i++) 61 { 62 items.Add(new Item { Key = i, Value = rnd.Next(0, 100) }); 63 } 64 itemsList.Add(items); 65 } 66 } 67 68 // ボタンクリックでグラフの生成 69 private void Button_Create_Click(object sender, RoutedEventArgs e) 70 { 71 int i = 0; 72 // グラフの数だけ描画する 73 foreach (var items in itemsList) 74 { 75 76 // LineSeries 生成 77 LineSeries lineSeries = new LineSeries() 78 { 79 ItemsSource = items, 80 IndependentValuePath = "Key", 81 DependentValuePath = "Value", 82 Title = i 83 }; 84 85 // SeriesにAdd 86 this.LineChart01.Series.Add(lineSeries); 87 88 i++; 89 } 90 } 91 92 // ボタンクリックでグラフのクリア 93 private void Button_Clear_Click(object sender, RoutedEventArgs e) 94 { 95 // SeriesのCollectionを初期化 96 this.LineChart01.Series.Clear(); 97 } 98 } 99}
次に、グラフ描画時間とメモリの測定です。
[1]. 本アプリをDebugモードで起動します。
[2]. 「グラフ生成」ボタン押下 → 「グラフクリア」ボタン押下 → 「グラフ生成」ボタン押下 → ・・・
と繰り返します。
[3]. この時の、各ボタンを押下してから処理が完了するまでの時間を、Debugモードの診断ツールのプロセスメモリグラフとCPUグラフの処理時間から読み取ります。
測定結果が以下です。(1枚目がシステム起動時、2枚目が上記の[2].を5分間繰り返したときの画像です)
赤丸の部分が、「グラフ生成」ボタンを押下してから、グラフが描画されるまでの時間で、
青丸の部分が、「グラフクリア」ボタンを押下してから、グラフが消えるまでの時間です。
上記のグラフから、以下のことが言えると考えました。
・メモリは起動時には急激に増加し、それ以降は、徐々に増加していく。
・処理時間は、グラフ描画時間はほとんど変わらないが、グラフクリアをする時間が5分経過時には、2倍近く遅くなっている。
とくに、処理時間が遅くなることが困っています。
アドバイス等ありましたらよろしくお願いします。
回答2件
あなたの回答
tips
プレビュー