C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[8]: 循環参照について


(過去ログ 26 を表示中)

[トピック内 12 記事 (1 - 12 表示)]  << 0 >>

■11716 / inTopicNo.1)  循環参照について
  
□投稿者/ 華緒里 (1回)-(2007/12/20(Thu) 14:33:05)

分類:[C#] 

(winXP,VS2005,C#)

現在、Formデザインの元となるFormBaseを作成し、
FormBaseを継承し画面を作成しています。

FormBase内で時計を動かすためTimerメソッドを使用しているのですが
コンストラクタでTimerのインスタンスを作成し、イベント宣言を行うと
画面遷移を行うたびにフォームが開放されずメモリリークが起きてしまっている状態です。

【FormBaseコンストラクタ】
private Timer secTimer;

public FormBase()
{
InitializeComponent();

//現在時刻の表示
lblTime.Text = getNowDateTime();

//タイマースタート
secTimer = new Timer();
secTimer.Interval = 1;
secTimer.Tick += new EventHandler(secTimer_Tick);
secTimer.Start();

}



コンストラクタでTimerメソッドを使用せず、デザイン時点でTimerコントロールを実装すれば
メモリリークせず動くことは確認できたのですが前者との違いがわかりません。
(コンストラクタ内にDesigner.csを呼び出すInitializeComponentがあるのに違いがわかりません。)

コンストラクタにTimerを実装する場合とデザイン時に実装する場合とで違いがあるのでしたら
教えていただきたいと思っています。

宜しくお願いいたします。
引用返信 編集キー/
■11717 / inTopicNo.2)  Re[1]: 循環参照について
□投稿者/ 囚人 (269回)-(2007/12/20(Thu) 14:35:19)
メモリリークしてるというのは、どうやって分かったんでしょうか?
引用返信 編集キー/
■11719 / inTopicNo.3)  Re[2]: 循環参照について
□投稿者/ 華緒里 (2回)-(2007/12/20(Thu) 14:47:09)
No11717 (囚人 さん) に返信
> メモリリークしてるというのは、どうやって分かったんでしょうか?

画面を遷移するたびにメモリが増えていき、
明示的にSystem.GC.Collect()を実行しても解消されませんでした。

またwindbg+SOSというソフトを使用したところ
画面をDisposeしても画面のインスタンスが残ったままだったので
メモリリークしていると確認しました。

Timerメソッドの部分をコメントアウトし実行してみたところ
ある一定まで増えた時点でガベージコレクションが働いていることが確認できました。
(タスクマネージャー上にて)
同じくwindbg+SOSを使用したところ画面をDispose後、インスタンスが残っていないことを
確認しました。


引用返信 編集キー/
■11722 / inTopicNo.4)  Re[1]: 循環参照について
□投稿者/ 渋木宏明(ひどり) (603回)-(2007/12/20(Thu) 15:02:08)
渋木宏明(ひどり) さんの Web サイト
> コンストラクタでTimerメソッドを使用せず、デザイン時点でTimerコントロールを実装すれば
> メモリリークせず動くことは確認できたのですが前者との違いがわかりません。
> (コンストラクタ内にDesigner.csを呼び出すInitializeComponentがあるのに違いがわかりません。)

InitializeComponent() のソースコードをよく読めばわかるはずです。

デザイナで timer を配置した場合、Form1.components.Add(タイマのインスタンス) が行われます。

これにより、Form1 のクローズと同期して timer の廃棄(IDisposable.Dispoe() 呼び出し)が行われるようになります。

引用返信 編集キー/
■11724 / inTopicNo.5)  Re[2]: 循環参照について
□投稿者/ 渋木宏明(ひどり) (604回)-(2007/12/20(Thu) 15:04:22)
渋木宏明(ひどり) さんの Web サイト
ところで、循環参照とはまったく関係のない話題だと思うんですが、どうしてこんな件名なんでしょうね?

引用返信 編集キー/
■11727 / inTopicNo.6)  Re[3]: 循環参照について
□投稿者/ 華緒里 (3回)-(2007/12/20(Thu) 15:32:41)
No11724 (渋木宏明(ひどり) さん) に返信
> ところで、循環参照とはまったく関係のない話題だと思うんですが、どうしてこんな件名なんでしょうね?

ありがとうございました。
デザイナで配置するとForm終了時に廃棄されるんですね。

調査時点ではメモリ内で循環参照してるのではないかと
思われていたので題名にしてしましました。

失礼いたしました。
引用返信 編集キー/
■11731 / inTopicNo.7)  Re[4]: 循環参照について
□投稿者/ 華緒里 (4回)-(2007/12/20(Thu) 16:46:09)
追加で質問すみません。

今回の原因はTimerをnewしたとき引数になにも設定されていなかったのが原因なのでしょうか?

this.secTimer = new System.Windows.Forms.Timer(this.components);

引数にあるthis.componentsはTimerのみかかれている項目だと思うのですが
他のコントロールと何が違ってくるのでしょうか?

すみませんが教えてください。

引用返信 編集キー/
■11734 / inTopicNo.8)  Re[5]: 循環参照について
□投稿者/ ぽぴ王子 (308回)-(2007/12/20(Thu) 17:17:51)
ぽぴ王子 さんの Web サイト
No11731 (華緒里 さん) に返信

> 追加で質問すみません。
> 
> 今回の原因はTimerをnewしたとき引数になにも設定されていなかったのが原因なのでしょうか?
> 
> this.secTimer = new System.Windows.Forms.Timer(this.components);
> 
> 引数にあるthis.componentsはTimerのみかかれている項目だと思うのですが
> 他のコントロールと何が違ってくるのでしょうか?
> 
> すみませんが教えてください。

ちょっと調べてみました。
フォームにタイマーコントロール(Timer1)を貼り付けて、Designer.cs ファイルを見てみたです。
要するに渋木さんのコレをちゃんと理解するためにね。

> InitializeComponent() のソースコードをよく読めばわかるはずです。
> 
> デザイナで timer を配置した場合、Form1.components.Add(タイマのインスタンス) が行われます。
> 
> これにより、Form1 のクローズと同期して timer の廃棄(IDisposable.Dispoe() 呼び出し)が行われるようになります。


他にもテキストボックス(textBox1)とかボタン(button1)なんかが貼り付けてあるわけだけど
そちらはこんな風に書かれてます。

>             this.Controls.Add(this.textBox1);
>             this.Controls.Add(this.button1);

で、タイマーはというと

>             this.components = new System.ComponentModel.Container();
>             this.timer1 = new System.Windows.Forms.Timer(this.components);

components という名前のコンテナを引数にしてますね。
MSDNを見ると「指定したコンテナがある Timer クラスの新しいインスタンスを初期化します。」
とあります。っつーことは、渋木さんの書いてた

> デザイナで timer を配置した場合、Form1.components.Add(タイマのインスタンス) が行われます。

が、タイマーだとこういう書き方になるんじゃないか、ってことですね。
この辺はたぶんタイマーコントロールはフォームの上におかれるようなクラスじゃないので、その
土台になるコンテナが必要ですよと。で、その辺りのクラスは他もそうだけど、コンストラクタで
土台を指定するんじゃないかな?

そして Dispose ではこんなコードが書かれています。

>             if (disposing && (components != null))
>             {
>                 components.Dispose();
>             }
>             base.Dispose(disposing);

ここでcomponents.Dispose()を呼ぶことで、それに乗っているタイマーのDisposeが呼ばれるということ
になるんじゃないか、と推測されます。

逆に言えば、もともとの書き方でもフォームのDispose時に責任を持ってタイマーのDisposeを呼ぶように
すれば大丈夫なのかもしれません。
(この辺は間違ってたらどなたかフォローお願いします)

引用返信 編集キー/
■11737 / inTopicNo.9)  Re[6]: 循環参照について
□投稿者/ 華緒里 (6回)-(2007/12/20(Thu) 17:45:25)
No11734 (ぽぴ王子 さん) に返信

>この辺はたぶんタイマーコントロールはフォームの上におかれるようなクラスじゃないので、その
土台になるコンテナが必要ですよと。で、その辺りのクラスは他もそうだけど、コンストラクタで
土台を指定するんじゃないかな?

デザインでおいた場合はフォーム上に定義されるものと勘違いしていました。
見た目ではデザインフォームにおかれていますが実際は画面のコントロールではないですよね。



> if (disposing && (components != null))
> {
> components.Dispose();
> }
> base.Dispose(disposing);

>ここでcomponents.Dispose()を呼ぶことで、それに乗っているタイマーのDisposeが呼ばれるということ
>になるんじゃないか、と推測されます。

もし修正前のソースのまま実行しようとした場合、画面.Disposeの前にTimerのDisposeが必要になりそうですね。

ありがとうございます。

引用返信 編集キー/
■11749 / inTopicNo.10)  Re[6]: 循環参照について
□投稿者/ 渋木宏明(ひどり) (605回)-(2007/12/20(Thu) 20:58:24)
渋木宏明(ひどり) さんの Web サイト
> とあります。っつーことは、渋木さんの書いてた
>
>>デザイナで timer を配置した場合、Form1.components.Add(タイマのインスタンス) が行われます。
>
> が、タイマーだとこういう書き方になるんじゃないか、ってことですね。

ごめんなさい、コントロールとごっちゃになっていました。

コンポーネントの場合は、コンテナの参照がコンポーネントのコンストラクタに渡されて、コンポーネントのコンストラクタ内で Form1.components.Add(this) するんでした。

これは、ユーザコンポーネントを1個作ってみればわかります。

> この辺はたぶんタイマーコントロールはフォームの上におかれるようなクラスじゃないので、その
> 土台になるコンテナが必要ですよと。で、その辺りのクラスは他もそうだけど、コンストラクタで
> 土台を指定するんじゃないかな?

上下はあまり関係ありません。

単に「コンポーネントはコンストラクタ引数でコンテナを与える仕様」でよろしいかと。

> ここでcomponents.Dispose()を呼ぶことで、それに乗っているタイマーのDisposeが呼ばれるということ
> になるんじゃないか、と推測されます。

components の型をヘルプで調べれば、確証を得られます。

> 逆に言えば、もともとの書き方でもフォームのDispose時に責任を持ってタイマーのDisposeを呼ぶように
> すれば大丈夫なのかもしれません。

大丈夫ですが、そのためにはデザイナコードを書き換える必要があります。

それよりは、コンストラクタでコンテナ(=Form1 の参照)を与えた方がいいでしょうね。

引用返信 編集キー/
■11754 / inTopicNo.11)  Re[7]: 循環参照について
□投稿者/ mあ@反省中 (35回)-(2007/12/21(Fri) 00:25:47)
2007/12/21(Fri) 00:46:14 編集(投稿者)
2007/12/21(Fri) 00:38:32 編集(投稿者)

secTimer = new Timer();
secTimer.Interval = 1;
secTimer.Tick += new EventHandler(secTimer_Tick);
secTimer.Start();

すんまそん。毎秒1000回の突入ありますけど。
1/1000の精度を表示する時計なんですか?
secTimer_Tick内処理が1ms未満で完結しないと・・・次が呼ばれて・・・

訂正:1000回→1000回未満
1/1000 →約1/1000

secTimer がsecondTimer の略ならば、Interval=1*1000 でよろしいのでは?

参考:
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=41176&forum=7

追記:
フォームデザイナー上にタイマーコントロールを落としてみました。
Interval のデフォルトは 100 でした。

本当にタイマーの精度が1ms必要なのか再検討してみた方がいいですよ。
参考リンクにも代替ではありませんが、ヒントが書いてあります。

引用返信 編集キー/
■11823 / inTopicNo.12)  Re[8]: 循環参照について
□投稿者/ とりこびと (39回)-(2007/12/21(Fri) 18:01:41)
とりこびと さんの Web サイト
とりこびとです。

>単に「コンポーネントはコンストラクタ引数でコンテナを与える仕様」でよろしいかと。

蛇足ですが、質問者の最初の投稿を元に

		private Timer secTimer;

		public FormBase()
		{
			InitializeComponent();

			secTimer = new Timer(this.components);
		}

上のように単純にthis.componentsを与えるとNullReferenceExceptionが発生したと思います。
デザイナ上から何かしらコンポーネントを配置していない場合(?)、this.componentsはNullのままになっていたと思いますので、
components も初期化しないといけなかったような気がしてます。(試せてません。)


引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -