none
アンマネージリソース と Dispose と GC の関係について RRS feed

  • 質問

  • GC と Dispose とアンマネージリソースの関係に関して質問です。

    先日、以下のスレッドにおいて回答したところ、多くの方に指摘頂き疑問に思ったのですが、

    PictureBoxの内容をClipboardへコピーするには

    .NET Framework で提供されている標準的なクラスにおいては


    1.アンマネージリソースを扱うクラスは IDisposable インターフェイスを継承し、Dispose メソッドを実装している。

    2.仮に Dispose が実行されなかった場合でも、Finalize メソッド内で Dispose をコールするよう設計されている。

    3.よって、ほとんどのアンマネージリソースは、アプリケーション終了時に GC によって安全に解放される筈である。


    と理解してもいいのでしょうか?

    また、「このクラスは明示的に Dispose をしないと危険」 というクラスも合わせて教えて頂けるとありがたいです。

    2009年11月16日 4:12
    モデレータ

回答

  • 1 は、「マネージドなメモリ以外のリソースを扱うクラス」とすべきでしょう。マネージリソース(要するに IDisposable を実装するメンバ)を持つクラスも一般的に IDisposable を実装するのが普通です(StreamWriter など)。
    ちなみに、「アンマネージリソースを持っている」=>「IDisposable を実装する」はほぼ正しいですが、逆の「IDisposable を実装する」=>「アンマネージリソースを持っている」は成立しません。「派生クラスでそういうリソースを扱うことが予想される基底クラス」(Stream など)や「Dispose パターンが扱いやすいから IDisposable を持っている」(IComponent など)といったパターンがあります。

    2 は、アンマネージリソースについてはその通りです(マネージリソースは、ファイナライザでは基本的に解放できません)。

    3 ですが、アプリケーション終了時のアンマネージリソースの解放はどちらかというと GC の仕事ではなく OS の仕事です。一応ファイナライザも解放処理を行いますが。
    Win32API の CreateFile を直接呼んでファイルハンドルを IntPtr で受け取っても、アプリケーション終了時にはファイルのロックは自動的に解除されます。ここに GC の動く余地はありません。
    ほかにも、強制終了されたときとか。
    また、「このクラスは明示的に Dispose をしないと危険」 というクラスも合わせて教えて頂けるとありがたいです。
    IDisposable を実装するもの全て、と言っておきましょう。
    // まあ静的メンバなんかは Dispose を呼び出すタイミングがないでしょうけど。
    2009年11月16日 5:08
  • Deleted
    2009年11月16日 5:15
  • 「このクラスは明示的に Dispose をしないと危険」 というクラスも合わせて教えて頂けるとありがたいです。
    基本的にはないはずですが、もし存在するようなら、クラスの説明かDispose()メソッドの説明に明記されていると思います。具体的な例については思いつきません。
    .NET Framework 2.0からは必ず処理しておきたいアンマネージリソースを扱うためにCriticalFinalizerObjectクラス とその派生クラスが用意されています。Mutexクラスなんかがこれを使っています。
    それからPerformanceCounterクラスのCloseSharedResourcesメソッド のようにどうにも解放できないものは別途メソッドを用意していたりします。
    # といってもPerformanceCounterクラスはバグバグで使わない方がいいです。
    2009年11月16日 11:15
  • Deleted
    2009年11月17日 15:07
  • Deleted
    2009年11月18日 1:29
  • Deleted
    2009年11月18日 2:31
  • Deleted
    2009年11月20日 1:44
  • この記述おかしいですね。
    DbConnection は、適用範囲外では閉じられません。そのため、機能的に同等なCloseまたはDisposeを呼び出して、明示的に接続を閉じる必要があります。接続プールの値Poolingがtrueまたはyesに設定されている場合、これにより、物理的な接続も解放されます。
    connection poolが有効なら、Close()してもpoolに返されるはず。英語版も同じく。

    ついでにComponentのFilnalize()がDispose(false)を呼んでいます。
    この辺り、IDisposableではなくIComponentに従う必要があります。
    クラス、コンポーネント、コントロール
    2009年11月18日 4:00

すべての返信

  • 1 は、「マネージドなメモリ以外のリソースを扱うクラス」とすべきでしょう。マネージリソース(要するに IDisposable を実装するメンバ)を持つクラスも一般的に IDisposable を実装するのが普通です(StreamWriter など)。
    ちなみに、「アンマネージリソースを持っている」=>「IDisposable を実装する」はほぼ正しいですが、逆の「IDisposable を実装する」=>「アンマネージリソースを持っている」は成立しません。「派生クラスでそういうリソースを扱うことが予想される基底クラス」(Stream など)や「Dispose パターンが扱いやすいから IDisposable を持っている」(IComponent など)といったパターンがあります。

    2 は、アンマネージリソースについてはその通りです(マネージリソースは、ファイナライザでは基本的に解放できません)。

    3 ですが、アプリケーション終了時のアンマネージリソースの解放はどちらかというと GC の仕事ではなく OS の仕事です。一応ファイナライザも解放処理を行いますが。
    Win32API の CreateFile を直接呼んでファイルハンドルを IntPtr で受け取っても、アプリケーション終了時にはファイルのロックは自動的に解除されます。ここに GC の動く余地はありません。
    ほかにも、強制終了されたときとか。
    また、「このクラスは明示的に Dispose をしないと危険」 というクラスも合わせて教えて頂けるとありがたいです。
    IDisposable を実装するもの全て、と言っておきましょう。
    // まあ静的メンバなんかは Dispose を呼び出すタイミングがないでしょうけど。
    2009年11月16日 5:08
  • Deleted
    2009年11月16日 5:15
  • 「このクラスは明示的に Dispose をしないと危険」 というクラスも合わせて教えて頂けるとありがたいです。
    基本的にはないはずですが、もし存在するようなら、クラスの説明かDispose()メソッドの説明に明記されていると思います。具体的な例については思いつきません。
    .NET Framework 2.0からは必ず処理しておきたいアンマネージリソースを扱うためにCriticalFinalizerObjectクラス とその派生クラスが用意されています。Mutexクラスなんかがこれを使っています。
    それからPerformanceCounterクラスのCloseSharedResourcesメソッド のようにどうにも解放できないものは別途メソッドを用意していたりします。
    # といってもPerformanceCounterクラスはバグバグで使わない方がいいです。
    2009年11月16日 11:15
  • 多くのご意見、誠にありがとうございます。<(_ _)>

    > ちなみに、「アンマネージリソースを持っている」=>「IDisposable を実装する」はほぼ正しいですが、
    > 逆の「IDisposable を実装する」=>「アンマネージリソースを持っている」は成立しません。
    > 「派生クラスでそういうリソースを扱うことが予想される基底クラス」(Stream など)や
    > 「Dispose パターンが扱いやすいから IDisposable を持っている」(IComponent など)といったパターンがあります。

    参考になります。
    確かに派生クラスで「リソースを扱うことが予想される」パターンというのは十分考えられますね。


    > 厳密には、単に「扱う」のではなく、アンマネージリソースを「カプセルする」クラスが対象です。

    なるほど・・・

    「アンマネージリソースをカプセル化する」

    これは大変しっくりくる表現です。頭にスッと入ってきます。

    最近パターンを「カプセル化」という観点から考えられないか、色々と考える機会が多いので、
    これは非常に判りやすい表現だと思います。ありがとうございます。


    皆様の返信は非常に面白い内容を含んでおりますが、
    ちょっと忙しくなってきましたので、続きはまた後でじっくり考えさせて頂きます。<(_ _)>

    2009年11月17日 1:49
    モデレータ
  • 2.仮に Dispose が実行されなかった場合でも、Finalize メソッド内で Dispose をコールするよう設計されている。

    Hongliang さん
    > アンマネージリソースについてはその通りです。

    渋木さん
    > そのようにクラスを設計することが「求められている」だけです。
    > ランタイム側でそれを保証するすべはありません。

    むmm? ここはどう捉えるべきでしょうか?
    Dispose がコールされなかった場合、本来は Finalize 内で Dispose をコールする筈だが、
    必ずしもそうなっているとは保証されてないと考えるべきでしょうか?
    2009年11月17日 8:28
    モデレータ
  • Deleted
    2009年11月17日 15:07
  • ちょっと昨日の質問では言葉が足りなくなってしまいましたが、
    そうです。だって、CLR は Finalizer を呼び出すだけですから。
    Finalizer 内で Dispose() を呼び出すのは、そのクラスの実装者の責任です。
    ということは・・・・・・

     「.NET Framework の標準的なクラス」  

    この場合の 「実装者」 である MicroSoft が公開している、マネージリソースを扱うクラスであっても
    Dispose() しなかった場合、Finalize 内で必ずしも Dispose がコールされているとは保証されてない

    と考えてもいいのでしょうか?


    Hongliang さん
    3 ですが、アプリケーション終了時のアンマネージリソースの解放はどちらかというと GC の仕事ではなく OS の仕事です。一応ファイナライザも解放処理を行いますが。
    Win32API の CreateFile を直接呼んでファイルハンドルを IntPtr で受け取っても、アプリケーション終了時にはファイルのロックは自動的に解除されます。ここに GC の動く余地はありません。
    ほかにも、強制終了されたときとか。

    渋木さん
    >3.よって、ほとんどのアンマネージリソースは、アプリケーション終了時に GC によって安全に解放される筈である。

    YES ですが、その動作が本当に不適当でないかどうかは場合によりけりです。

    >また、「このクラスは明示的に Dispose をしないと危険」 というクラスも合わせて教えて頂けるとありがたいです。

    前項と重複しますが、危険≒不適当かどうかは場合によりけりな面もあるので、なんとも言い難いです。

    なるほど、確かに強制終了された場合は、GC の関与する余地はありませんよね。また場合によりけりと。。。
    これで GC は万能ではないということがよく判りました。


    佐祐理さん

    > 基本的にはないはずですが、もし存在するようなら、クラスの説明かDispose()メソッドの説明に明記されていると思います。
    > 具体的な例については思いつきません。

    データベースコネクション絡みは以前から気になっていたので、改めて調べてみました。
    DbConnection は明示的に Close もしくは Dispose しろと書いてありました。以下引用です。
    DbConnection は、適用範囲外では閉じられません。そのため、機能的に同等な Close または Dispose を呼び出して、明示的に接続を閉じる必要があります。接続プールの値 Poolingtrue または yes に設定されている場合、これにより、物理的な接続も解放されます。
    2009年11月17日 17:08
    モデレータ
  • Deleted
    2009年11月18日 1:29
  • >この場合の 「実装者」 である MicroSoft が公開している、マネージリソースを扱うクラスであっても
    >Dispose() しなかった場合、Finalize 内で必ずしも Dispose がコールされているとは保証されてない
    >と考えてもいいのでしょうか?

    そうです。あったらイヤですが。

    なるほど・・・よくわかりました。

    「Dispose をコールしている筈だけど、必ずしも保証はないよ」

    と理解しておきます。(^ω^;

    >DbConnection は明示的に Close もしくは Dispose しろと書いてありました。

    微妙。

    この場合、Close() や DIspose() を明示的に呼び出すことが求められているというよりも、不要になった接続は速やかに解放するべし、くらいの意味のような気がします。
    (不安なら、原文も確認してみてください。僕は見てないけど (^^;)
    英文の方を覗いてみました。

    「you must explicitly close the connection by calling Close or Dispose」

    must explicitly ・・・せねばならない・・・明らかに・・・

    これはかなり強い表現ですね。(^ω^;


    私としては try ~ finally や using で、
    必ず Close もしくは Dispose を実行するようコーディングを心がけてますが、
    (もちろん、インスタンスの寿命が管理されている場合は除きますが・・・)
    先日の佐祐理さんのレスで、初めて Environment.Exit で
     finally ブロックを通過せずにアプリケーションを強制終了できてしまうことを知りました。

    Environment.Exit 以外にも try ~ finally  等が無効になるケースってあるでしょうか?
    2009年11月18日 2:07
    モデレータ
  • Deleted
    2009年11月18日 2:31
  • この記述おかしいですね。
    DbConnection は、適用範囲外では閉じられません。そのため、機能的に同等なCloseまたはDisposeを呼び出して、明示的に接続を閉じる必要があります。接続プールの値Poolingがtrueまたはyesに設定されている場合、これにより、物理的な接続も解放されます。
    connection poolが有効なら、Close()してもpoolに返されるはず。英語版も同じく。

    ついでにComponentのFilnalize()がDispose(false)を呼んでいます。
    この辺り、IDisposableではなくIComponentに従う必要があります。
    クラス、コンポーネント、コントロール
    2009年11月18日 4:00
  • 渋木さん
    > この場合の「保証はない」は、可能性の話です。
    > ・・・中略・・・
    > そのような実装は、普通に考えて(今度こそほんとの)リソースリークを引き起こすことが予想されるため、「設計/実装ミス」であると考えられます。

    なろほどです。


    > ワーカースレッドに対して、Thread.Abort をしつこく何度もかけたときとか?

    マルチスレッドはかな~り昔・・・Delphi で少しやっただけなので、かなり忘れてますが、
    機会見てこの辺も色々調べてみたいと思います。


    佐祐理さん
    > connection poolが有効なら、Close()してもpoolに返されるはず。英語版も同じく。

    そうなんですか?コネクションプールは概略しか掴めてないので、この辺イマイチピンと来てないのが現状です。
    これも機会を見てしっかり抑えたいと思います。

    あと
    > クラス、コンポーネント、コントロール

    参考になるリンクありがとうございます。<(_ _)>
    おかげでかなり GC と Dipose と アンマネージリソースの関係について判ってまいりました。


    いずれにせよ皆様貴重な情報ありがとうございました。
    まだ一部のリンク先を読み切れてない状況ですが、いったんこれにてこのスレを収束させていただきます。
    また判らない点、不明な点が出てきたら、新規にスレッド起して質問させて頂きたいと思います。<(_ _)>

    2009年11月19日 16:09
    モデレータ
  • Deleted
    2009年11月20日 1:44
  • > System.ComponentModel の話など、IDisposable.Dispose() より上流の話も出てしまいましたが、下のレイヤから順に消化していくことをお勧めします。

    ・・・(中略)・・・

    > と、まずはここまでが .NET におけるアンマネージリソース(をカプセルするオブジェクトインスタンス)の寿命管理を考える上での根幹になります。

    > それ以外の議論は、すべて上記の基本理念に基づいて導き出されたもの(あるいは、そこからさらに導きされたもの)であると思います。

    おぉ!これはありがとうございます!!(^ω^)
    このレスを参考に、もう一回スレッド全般並びに関連記事をおさらいしたいと思います。
    本当にありがとうございました!!

    2009年11月20日 2:01
    モデレータ