none
Windows Formを開く度にNewするのは負荷になりませんか? RRS feed

  • 質問

  • みなさんこんにちは。

    Windows Formを使ってアプリケーションを作っています。

    そこで一般的な画面の開き方は次のようなコーディングになると思うのですが、

    開くたびにFormをNewするのは負荷になりませんか?


    Dim nextForm = new NextForm()
    nextForm.Show()

    実行してみると初回にFormを開く時には時間がかかっていますが、

    2回目以降はすぐにFormが表示されています。

    • 1回目はDLLをメモリ空間にロードしているから時間がかかる?
    • 2回目以降はロード済みだから新たなForm分のメモリ空間の確保だけなので、時間がかからない?
    • 1度NewしたFormをキャッシュするなどの処理は効果あるのか?

    大量のFormを使ったアプリケーションを作りたいと思いますので、把握しておきたいのです。

    ご教示お願いいたします。

     

    2006年8月17日 2:36

すべての返信

  • 1回目2回目というのは、アプリケーション起動の回数?
    New の回数?

    前者の意味ならば、多分OSの機能かな。プリフェッチされたり何なり…。
    後者の意味ならば、そんな事はないと思うのですが。

    逐次 new しないで、インスタンスを保持しておくというのはシナリオ次第では意味のある事だとおもいます。
    特にフォームというのは、クラス1つにインスタンス1つという関係が多数だと思うので、有効ではないでしょうか。

    2006年8月17日 3:50
  • 囚人さんご回答ありがとうございます。

     囚人 さんからの引用

    1回目2回目というのは、アプリケーション起動の回数?
    New の回数?

    前者の意味ならば、多分OSの機能かな。プリフェッチされたり何なり…。
    後者の意味ならば、そんな事はないと思うのですが。

    1回目、2回目というのはFormの表示回数のことです。つまりNewを実行している回数です。

    1回目は3秒程度かかるのですが、2回目以降は一瞬です。

    Newする本来の意味であれば毎回3秒程度かかって当然で、つじつまが合わない気がしますが。

    特別な理由があるのでしょうか?

    私の思いとしては、次のような効果を上げたいのですが、、、

    • マシンに負荷をかけない。(CPU、メモリを無駄に消費させない。)
    • 低スペックのマシンでも快適に動作する。
    • Formを開くためにかかる時間を短くする。

    つまりはクライアントアプリケーションのパフォーマンスチューニングですかね。。。

    2006年8月17日 6:51
  • CLRは必要な部分だけネイティブコードに変換するので1回目はその分時間がかかり、2回目以降は既に変換されているのでその分の時間が短縮されるのではないでしょうか。

    2006年8月17日 7:11
  • CLRは必要な部分だけネイティブコードに変換するので1回目はその分時間がかかり、2回目以降は既に変換されているのでその分の時間が短縮されるのではないでしょうか。

    あ、それだ。忘れていました。
    どうもありがとうございます。

    2006年8月17日 7:30
  •  cat-walk さんからの引用

    CLRは必要な部分だけネイティブコードに変換するので1回目はその分時間がかかり、2回目以降は既に変換されているのでその分の時間が短縮されるのではないでしょうか。

    つまりNewされる処理自体は何も変わらないということですね。

    ならばキャッシュすることに意味があるかもしれませんが、

    NewしてFormを開く処理をタスクマネージャで監視してもあまり変化がありません。

    Windowsアプリケーションのパフォーマンスを上げるという視点ではどうするべきなんでしょうか?

    2006年8月17日 8:21
  • がりがりにパフォーマンスをあげたいのですか?

    そこまで必要なのですか?

    終了処理は自前で責任をもてますか?

    ここまでYesであれば、Newしたインスタンスを持ちまわるといいです。

    毎回あげるたびに初期化しておきたいのであれば、そのフォームの初期化メソッドを作って、まるでインスタンスを作ったかのようにふるまえばいいのです。

    2006年8月17日 11:59
  • 2回目は一瞬でNewできるならあえてやる必要はないことが多いのでは?
    タスクマネージャについては何を見ているのかよくわかりませんが。

    パフォーマンスといっても、一概にこうすればいいといえるものではありません。
    メモリ節約なのか、CPU節約なのか、体感速度のみ重視なのか…

    で、体感で分からないレベルでチューニングが必要なんてなると、
    そもそも.NETでやるのがどうかって話になりかねません。

    ま、後は中さんがおっしゃってるような感じですかね。

     

    2006年8月17日 12:58
  • Showする回数でなく、Newする回数の1回目、2回目で速度が違うことが確実なら
    あえてキャッシュする必要はないですよね。
    キャッシュしなくても2度目以降のNewは早いわけですから。

    それでも、体感未満の性能まで追求したいのであればキャッシュは有効かもしれませんが、
    開放のタイミングが難しいような。
    弱参照を使う手もあるけど、有効な参照か判定する方がNewよりも重くなる可能性もあるし、、
    アプリの終了時まで放置しておく方法もあるけど大量のFormを使うってことなのでメモリの無駄遣いですよね^^;


    1度目のNewが遅いのが、ネイティブコードへの変換が原因かは、
    ngen.exeでFormを含むアセンブリを事前に変換してしまえばわかりますね。

    2006年8月17日 14:08
  •  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の場合なら,同じ理屈で,二度目は速いだろうから,
    クリアする処理を入れるのと大差がないような気もします。たぶん。

    2006年8月17日 21:17