質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

Q&A

解決済

3回答

7562閲覧

WPF ウィンドウを閉じた際のImageコントロールの解放方法

kawauso

総合スコア56

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

0グッド

0クリップ

投稿2017/08/23 01:07

いつも、お世話になっております。
親ウィンドウから子ウィンドウを開き、子ウィンドウが開かれた時点でImageコントロールを複数作成して子ウィンドウに配置したいと思っております。
配置する複数のImageコントロールは、開かれる度にPathや数が変化し、コードビハインド側で操作したいので、コードビハインド側で配置しています。

問題点
(1)子ウィンドウを閉じた際に、Imageコントロールが解放されずメモリ上に残ってしまう。
(2)そのため、子ウィンドウを閉じる→開くを繰り返した際に、メモリに蓄積されてしまう。

試してみたこと
・子ウィンドウのClosingイベントでGC.Collect();を行う。
親 起動時 55MB
子 起動 81MB
子 終了 78MB
子 起動 103MB
子 終了 78MB
子 起動 103MB
子 終了 78MB
このように子ウィンドウ初回終了時は、メモリが解放されることはなく、
2回目以降は、ほぼきちんとメモリ解放できていますが、稀に解放できないときがあります。

質問
・ウィンドウ終了時にImageコントロールを確実に破棄する方法を教えていただきたいです。
・なぜ初回終了時はメモリが解放されないのでしょうか。(子ウィンドウのInstanceプロパティあたりの実装方法に問題があるのでしょうか?)

環境
C#, .NET 4.6, VS2017 Pro

コード
親Windowからはvar cw = ChildWindow.Instance; cw.Show(); cw.Owner = this;このように開いています。

C#

1 public partial class ChildWindow: Window 2 { 3 #region ユニークなウィンドウ プロパティ 4 private static ChildWindow _instance; 5 6 public static ChildWindow Instance 7 { 8 get 9 { 10 if (_instance == null || !_instance.IsLoaded) 11 { 12 _instance = new ChildWindow(); 13 } 14 15 return _instance; 16 } 17 } 18 #endregion 19 20 private ObservableCollection<Image> Images; 21 private ObservableCollection<string> PathList { get; } = new ObservableCollection<string> 22 { 23 @"test0.JPG", 24 @"test1.JPG", 25 @"test2.JPG", 26 @"test3.JPG" 27 }; 28 29 public ChildWindow() 30 { 31 InitializeComponent(); 32 33 Images = new ObservableCollection<Image>(); 34 foreach (var p in PathList) 35 { 36 Images.Add(CreateImage(p)); 37 } 38 39 foreach (var i in Images) 40 { 41 grid.Children.Add(i); 42 } 43 44 this.Closing += (s, e) => 45 { 46 // あまり意味はないかもしれません 47 foreach (var i in Images) 48 { 49 grid.Children.Remove(i); 50 } 51 52 GC.Collect(); 53 }; 54 } 55 56 private Image CreateImage(string path) 57 { 58 Image img = new Image(); 59 img.Visibility = Visibility.Collapsed; 60 61 using (FileStream stream = File.OpenRead(path)) 62 { 63 BitmapImage bi = new BitmapImage(); 64 bi.BeginInit(); 65 bi.StreamSource = stream; 66 bi.CacheOption = BitmapCacheOption.OnLoad; 67 bi.CreateOptions = BitmapCreateOptions.None; 68 bi.DecodePixelHeight = 1000; 69 bi.EndInit(); 70 bi.Freeze(); 71 72 img.Source = bi; 73 } 74 75 return img; 76 } 77 }

ChildWindowのXAMLは<Grid Name="grid"/>のみです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ebiryo

2017/08/23 03:09

念のため確認なのですが、「なぜ初回終了時はメモリが解放されないのか」というのは、子が終了したときに 「親 起動時 55MB」にならないのはなぜか?という意味でしょうか?
kawauso

2017/08/23 03:13

はい。そういう意味で書かせていただきました。
guest

回答3

0

cwが閉じた時に「親 起動時 55MB」にならないのは、cw配下のImageはClose時にGCで改修されますが、cw自体がGCによって回収されていないからでは? IYEMON018さんのやり方プラス以下でどうでしょうか。

C#

1var cw = new ChildWindow(); 2cw.Show(); 3cw.Owner = this; 4 5//子が閉じたら 6cw = null; 7GC.Collect()

※厳密にこだわる必要ない気もしますが。。

投稿2017/08/23 03:33

ebiryo

総合スコア797

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kawauso

2017/08/25 00:17

回答ありがとうございます。 cwのClosedイベントで試してみましたが、解消はされませんでした。 >>※厳密にこだわる必要ない気もしますが。。 初回以降はきちんと解消されているので、そうします。
guest

0

ベストアンサー

「試してみたこと」で計測しているメモリ量はどのように計測しているのでしょうか?
もし、アプリケーション全体のメモリ使用量を計測しているのであれば、Image ではなく以下の箇所が原因では?

cs

1var cw = ChildWindow.Instance; 2cw.Show(); 3cw.Owner = this;

なぜ、static のインスタンスを生成しているのかよくわからないです。
初回のみメモリ量が増加しているのはこのstatic インスタンスを生成した分のメモリが蓄積されているだけではないでしょうか。
2回目以降はインスタンスを使いまわしているのでメモリ量に変化が無いのでは。

cs

1var cw = new ChildWindow(); 2cw.Show(); 3cw.Owner = this;

こちらで試しても同じくメモリは解消されないのでしょうか?

投稿2017/08/23 02:29

IYEMON018

総合スコア202

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kawauso

2017/08/23 02:47

回答ありがとうございます。 メモリ計測に関してはVisualStudioの診断ツール>プロセスメモリの値を見ての判断ですので、 アプリケーション全体のメモリ使用量になると思います。 staticなインスタンスを作成している理由は、ウィンドウを1つしか表示させないようにしたかったからです。 ボタンを連打して複数の子ウィンドウが表示されるのを防ぎたかった為です。下記を参考にしています。 https://dobon.net/vb/dotnet/form/singleform.html アドバイスいただいた var cw = new ChildWindow(); cw.Show(); cw.Owner = this; を試してみましたが、メモリは解消されませんでした。
IYEMON018

2017/08/23 03:31

解消されませんでしたか…、見当違いでしたね。 では、ChildWindow のClosing で以下の様にしてみても解消されないでしょうか? '''cs Images.Clear(); Images = null; PathList.Clear(); PathList = null; '''
kawauso

2017/08/25 00:04

解消はされませんでした。 本来のコードはPathListは別の親ウィンドウから参照渡しで渡しているので、そのあたりが原因なのかもしれません。 2回目以降はきちんと解消できているようです。 ebiryoさんより「こだわる必要もない..」と回答いただきましたので、解決済みとしたいと思います。
guest

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問