質問者
Windows Formを開く度にNewするのは負荷になりませんか?

質問
-
みなさんこんにちは。
Windows Formを使ってアプリケーションを作っています。
そこで一般的な画面の開き方は次のようなコーディングになると思うのですが、
開くたびにFormをNewするのは負荷になりませんか?
Dim nextForm = new NextForm()
nextForm.Show()
実行してみると初回にFormを開く時には時間がかかっていますが、
2回目以降はすぐにFormが表示されています。
- 1回目はDLLをメモリ空間にロードしているから時間がかかる?
- 2回目以降はロード済みだから新たなForm分のメモリ空間の確保だけなので、時間がかからない?
- 1度NewしたFormをキャッシュするなどの処理は効果あるのか?
大量のFormを使ったアプリケーションを作りたいと思いますので、把握しておきたいのです。
ご教示お願いいたします。
すべての返信
-
囚人さんご回答ありがとうございます。
囚人 さんからの引用 1回目2回目というのは、アプリケーション起動の回数?
New の回数?前者の意味ならば、多分OSの機能かな。プリフェッチされたり何なり…。
後者の意味ならば、そんな事はないと思うのですが。1回目、2回目というのはFormの表示回数のことです。つまりNewを実行している回数です。
1回目は3秒程度かかるのですが、2回目以降は一瞬です。
Newする本来の意味であれば毎回3秒程度かかって当然で、つじつまが合わない気がしますが。
特別な理由があるのでしょうか?
私の思いとしては、次のような効果を上げたいのですが、、、
- マシンに負荷をかけない。(CPU、メモリを無駄に消費させない。)
- 低スペックのマシンでも快適に動作する。
- Formを開くためにかかる時間を短くする。
つまりはクライアントアプリケーションのパフォーマンスチューニングですかね。。。
-
Showする回数でなく、Newする回数の1回目、2回目で速度が違うことが確実なら
あえてキャッシュする必要はないですよね。
キャッシュしなくても2度目以降のNewは早いわけですから。それでも、体感未満の性能まで追求したいのであればキャッシュは有効かもしれませんが、
開放のタイミングが難しいような。
弱参照を使う手もあるけど、有効な参照か判定する方がNewよりも重くなる可能性もあるし、、
アプリの終了時まで放置しておく方法もあるけど大量のFormを使うってことなのでメモリの無駄遣いですよね^^;
1度目のNewが遅いのが、ネイティブコードへの変換が原因かは、
ngen.exeでFormを含むアセンブリを事前に変換してしまえばわかりますね。 -
soymilk さんからの引用 Newする本来の意味であれば毎回3秒程度かかって当然で、つじつまが合わない気がしますが。 特別な理由があるのでしょうか?
インスタンスというのは,.NET的には,
同期用のインデックス(4バイト)と
メソッドテーブルへのポインタ(4バイト)と
インスタンスフィールドのバイト数合計 + α (padding分)だけです。インスタンスごとに作成されます。
(インスタンスフィールドの文字列は,長さにかかわらず4バイトになります。
文字列の実体は,別の場所に確保されます。
さらに,リテラル文字列の場合は,あらかじめプーリングされてます。)一方,
メソッドテーブルや(その上に存在する)静的フィールドや
EEClass (自クラス情報, reflection info, COMでいうところの Class Object)
と呼ばれるもの等は,クラスで,ひとつになります。で,最初にクラスローダでロードされるEEClassと,
それに続く Method Table は,一度ロードされたらずっと残ります。一回目で時間がかかるのは,主に,
継承元クラスも含めたそれらEEClassのインスタンスと
メソッドテーブルのインスタンス作成にでしょう。(クラスライブラリは,すでにネイティブコンパイルされているので
それらクラスはJITと関係ないのと,
インスタンス作成時にあまり関係ない自クラスのメソッドは,
メソッド実行時にJITされるんじゃないかと思います。
一度JITされれば,
Method Table上に存在している
(メソッドをJITコンパイルするための情報先への)ポインタは,
JITされたものの番地へと入れ替わる(.NET2.0以降)ので,
これも関係なくなります)なので,二回目以降は,
その Form がどれだけインスタンスフィールドを抱えているかによります。
テキストボックスが大量に乗っかっているとかだとあれかもしれませんが,
ふつうは,毎回作成しても問題ないような気がします。それに,インスタンスをずっと残す場合,
インスタンスフィールドが握っている参照先も開放されなくなります。
そのあたりをこまめに参照を切ってやらないといけない場合もでてくることを考えると,
結構面倒な気がします。
(もちろん,ローカル変数は,参照を切る必要はないです。)VB6.0の時の ADO の Recordset は,
レコードの集合がそのRecordsetのインスタンスの中に詰まっていたわけでなく,
Recordsetのインスタンスは,
インターフェイスポインタを握っているだけの仕組みになっていました。
でも,
.NET 時代の DataTable は,そこに本当にデータが存在するので,
必要がなくなったら参照をきっておく必要があります。
特に,Formのインスタンスメンバで参照している場合で,
Formを破棄しないままにしておく場合などにです。型付きDataSetの場合なら,同じ理屈で,二度目は速いだろうから,
クリアする処理を入れるのと大差がないような気もします。たぶん。