- PR -

Form上のコントロールのDispose()について

投稿者投稿内容
momomo
会議室デビュー日: 2006/01/12
投稿数: 6
投稿日時: 2008-09-25 19:03
Compact Framework 2.0環境での話です。
OSはWindows CE 5.0です。

作成したFormアプリケーションを使用しているとOutOfMemoryExceptionが発生することがあります。
調査した結果、画面遷移を繰り返すと発生するらしいということで、以下のようなテストアプリケーションを作成してみました。

Form1がForm2を延々呼び出しては破棄する、という内容です。

----Form1.cs----
public partial class Form1 : Form
{
private void button1_Click(object sender, EventArgs e)
{
while (true)
{
Form2 fm2 = new Form2();
fm2.ShowDialog();
fm2.Dispose();
GC.Collect();
}
}
}

----Form2.cs----
// Form2には120個くらいのComboBox(メモリ消費を早くするため)を貼り付けています。
public partial class Form2 : Form
{
// TimerによりTickイベント発生→画面を閉じる。
private void timer1_Tick(object sender, EventArgs e)
{
this.Close();
}
}

上記コードで試してみたところ、メモリ消費量は徐々に増加していきました。
(コンパネのメモリから確認しただけですが、Collect()しているからCollect対象外のメモリサイズを見ているのと「≒」かなと)

それで、いろいろと考えた挙句、次のようにDispose()内に、画面上の全コントロールに対してDispose()を発行してみたところ、メモリ増加する現象が改善されることが確認できました。

----Form2.Designer.cs----
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
// ここで全ComboBoxに対してDispose()を実行
comboBox1.Dispose(); // ←ここを追加
comboBox2.Dispose(); // ←ここを追加
: // ←ここを追加
: // ←ここを追加
base.Dispose(disposing);
}

ここで皆様のご意見をいただきたいのです。
@こういったコーディングで解決するのは一般的なのでしょうか?
Aもしくはこういったコーディングは必須なのでしょうか?
B他によい方法はないでしょうか?

#WinCEOS搭載のデバイスということで、使用可能なメモリが小さいという制約があるのと、長時間アプリケーションが終了することがないため、メモリ管理に気を使っています。

#http://msdn.microsoft.com/ja-jp/library/fs2xkftw(VS.80).aspx
あたりも見てみましたが、サンプルではアンマネージなリソースを解放する例はありますが、フォーム上のコントロールまで破棄するサンプルというのを見たことがないので、どうしたものか悩んでいます。コントロールはComponent継承なのでIDisposableだから当然必要な対処なんだろうか、とか。。。悶々。

よろしくお願いします。
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2008-09-25 22:09
引用:

momomoさんの書き込み (2008-09-25 19:03) より:
上記コードで試してみたところ、メモリ消費量は徐々に増加していきました。
(コンパネのメモリから確認しただけですが、Collect()しているからCollect対象外のメモリサイズを見ているのと「≒」かなと)


 この辺を探していただければ、その手の話題はゴロゴロしています。


引用:

ここで皆様のご意見をいただきたいのです。
@こういったコーディングで解決するのは一般的なのでしょうか?
Aもしくはこういったコーディングは必須なのでしょうか?
B他によい方法はないでしょうか?


1.一般的ではない。
2.必須ではない。
3.他のことでメモリを消費していると思われます。BCL を疑うより、ご自分のプログラムを疑う方が先かと思います。

引用:

#http://msdn.microsoft.com/ja-jp/library/fs2xkftw(VS.80).aspx
あたりも見てみましたが、サンプルではアンマネージなリソースを解放する例はありますが、フォーム上のコントロールまで破棄するサンプルというのを見たことがないので、どうしたものか悩んでいます。コントロールはComponent継承なのでIDisposableだから当然必要な対処なんだろうか、とか。。。悶々。


 コンテナ コントロールに所属するコントロールは、コンテナ コントロールの Dispose メソッド内で Dispose するため、不要です。Controls プロパティによって、所属するコントロールにアクセスできます。一つ一つ、コントロールを列挙しなくても、Controls プロパティを列挙すれば済みます。

詳しいことは、
囚人さん blogs.wankuma.com/shuujin/
菊池さん www.ailight.jp/blog/kazuk/
NyaRuRuさん d.hatena.ne.jp/NyaRuRu/
のところを検索すれば見つかるでしょう。「dispose site:blogs.wankuma.com/shujin」
よしも
会議室デビュー日: 2008/09/30
投稿数: 2
投稿日時: 2008-09-30 16:57
Windows CE 5.0の.NET Framework 2.0(C#)で、
同様の問題ではまったことがあります。

悪い例:
Form form = new HogeForm();
form.ShowDialog();
form.Dispose();

良い例:
Form form = new HogeForm();
form.ShowDialog();
form.Dispose();
form = null;

最後にnullを設定しないと、このフォームがガベージコレクトされず、
メモリリークします。

Microsoftのサポートに問い合わせて回答をもらいましたので、
確かですが、そちらの環境の原因と同じかどうかはわかりません。
とりあえず試してみてください。

#…と同じ内容をslashdotでも投稿しました
じゃんぬねっと
ぬし
会議室デビュー日: 2004/12/22
投稿数: 7811
お住まい・勤務地: 愛知県名古屋市
投稿日時: 2008-09-30 19:39
引用:

よしもさんの書き込み (2008-09-30 16:57) より:

最後にnullを設定しないと、このフォームがガベージコレクトされず、メモリリークします。


はつみみです。 参照の解放でガベージコレクトのされやすさを期待できるならともかく、まったくガベージコレクトされず "メモリリーク" するなんてことはないと思うのですが... しかも 2.0 で、ですか... '広域な変数でかつリソースを食っている変数' など、もっと限定的な状況のお話ではないのでしょうか?

本当の話であれば私の作ったアプリケーション全滅w
_________________
C# と VB.NET の入門サイト
じゃんぬねっと日誌
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2008-09-30 21:32
とりあえず、

.net framework 2.0 gc compact site:support.microsoft.com

では、該当するものは出てきませんでした。
よしも
会議室デビュー日: 2008/09/30
投稿数: 2
投稿日時: 2008-10-01 13:29
私が書いたメモリリークの件ですが、現象が出るのはFormクラスだけです。
他のクラスでは問題ないようです。

限定的な話なのかもしれませんが、Microsoftの有償サポートに
問い合わせたところ、Microsoft側でも「現象を確認した」との返事が
ありましたので、やっぱり確かなんだと思います。

現象が発生した環境は、
SHARP RZ-1501
Windows CE v5.00 Build1400 On Aug 28 2006
Visual Studio 2005 SP1
Microsoft .NET Compact Framework 2.0 SP1
です。

逆に、他の環境でも現象が再現するか試して頂けると助かります。

ボタンがいくつかあるだけのFormを作り、
(a) ShowDialogとDisposeをひたすら繰り返す
(b) ShowDialogとDisposeとnull設定をひたすら繰り返す
プログラムで、メモリの減り具合を観察してみてください。

Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2008-10-01 21:04
引用:

momomoさんの書き込み (2008-09-25 19:03) より:
Compact Framework 2.0環境での話です。
OSはWindows CE 5.0です。

作成したFormアプリケーションを使用しているとOutOfMemoryExceptionが発生することがあります。
調査した結果、画面遷移を繰り返すと発生するらしいということで、以下のようなテストアプリケーションを作成してみました。

Form1がForm2を延々呼び出しては破棄する、という内容です。

----Form1.cs----
public partial class Form1 : Form
{
private void button1_Click(object sender, EventArgs e)
{
while (true)
{
Form2 fm2 = new Form2();
fm2.ShowDialog();
fm2.Dispose();
GC.Collect();
}
}
}

----Form2.cs----
// Form2には120個くらいのComboBox(メモリ消費を早くするため)を貼り付けています。
public partial class Form2 : Form
{
// TimerによりTickイベント発生→画面を閉じる。
private void timer1_Tick(object sender, EventArgs e)
{
this.Close();
}
}

上記コードで試してみたところ、メモリ消費量は徐々に増加していきました。
(コンパネのメモリから確認しただけですが、Collect()しているからCollect対象外のメモリサイズを見ているのと「≒」かなと)

それで、いろいろと考えた挙句、次のようにDispose()内に、画面上の全コントロールに対してDispose()を発行してみたところ、メモリ増加する現象が改善されることが確認できました。

----Form2.Designer.cs----
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
// ここで全ComboBoxに対してDispose()を実行
comboBox1.Dispose(); // ←ここを追加
comboBox2.Dispose(); // ←ここを追加
: // ←ここを追加
: // ←ここを追加
base.Dispose(disposing);
}


しまつた。。。改善されるのは当たり前じゃないか。
IDisposable インターフェイスで、「管理しなければならないものがある」ことを示したとき、管理を行うのは開発者です。対して、GC によって管理されるメモリは、GC が管理するので、開発者は管理できません。
それをおさえて、このコードをよく見て、よく考えよう。改善されるのは当たり前です。

で、何が間違っているかというと、GC.Colect をしていること。このコードに関しては、よしもさんが書かれている通り、GC.Colect の前で、null 参照してやる必要があります。これは正しい仕様で、その通り実装されているので、バグではありません。


改善される理由:
Dispose が引数付きであることに注目して下さい。その引数は、どんなときに、どんな値になるでしょうか。
しかし、fm2.Dispose() と、引数なしで実行しています。どうしてでしょう?


バグではない理由:
参照していると、回収されません。Dispose は、メモリの破棄ではありません。
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2008-10-01 21:25
すみません
引数の方は、勘違いです。

スキルアップ/キャリアアップ(JOB@IT)