[csharpll:0328] Re: <教> リソース解放のタイミング
- Subject:
- [csharpll:0328] Re: <教> リソース解放のタイミング
- From:
- Kouji Suzuki <k@...>
- Date:
- Mon, 20 Mar 2006 18:01:07 +0900
- X-Mailer:
- Microsoft Outlook IMO, Build 9.0.6604 (9.0.2911.0)
- Message-Id:
- <LNEFLJOMMEELLFMOGANPOEJJDOAA.k‐at‐kekyo.net>
- In-Reply-To:
- 316
kekyo@お久しぶりです。
> C#にはあるみたいやけど > デストラクタ
C#のデストラクタは構文上はC++のデストラクタと同じですが、
コンパイルすると Finalize() になります。
ファイナライザ(とDispose)の実装がJavaと違ってややこしく
感じるのは、アンマネージリソースの制御を支配できるからです。
Dispose()が明示的に呼び出された場合、そのクラス内で暗黙に
(有効な)他のオブジェクトへの参照を保持しているか、
あるいは全く保持していないので、Dispose()の実装は安全に
動作することができます。
しかし、Finalize()内ではもはやクラスで保持している参照は
無効となっている可能性があるため、うかつに操作出来ません。
#なんだったか、そこで無効な参照を操作すると例外が出た
#ような気がしますが未確認。
では、Finalize()では何もできないのかというとそうではなく、
無効な参照さえ使用しなければ、アンマネージリソースの解放が
出来ます。
たとえば、IntPtr型で保持した値は参照ではなく値型なので
これを使って解放できます。
#ファイルハンドル類とかメモリのハンドルはIntPtrに入って
#いるので、CloseHandle() みたいなことをすればよい。
この問題とDispose()の実装をうまくするために、プログラミング
..NET Framework P.475で示されるような実装を行うことを
推奨しています。
Dispose()は明示的に呼び出さない限りは、GCが呼び出すことは
ありません(C#ではusingのブロックを出るときにDispose()を
呼び出すfinalizeを生成できるけど同じです)。
GCがIDisposableを実装していることを検知して、自動的に
Dispose()を呼び出してくれれば良いのに、と思ったことも
ありますが、以上のような理由で
「そのタイミングでDispose()が呼ばれても困ったチャンになる」
ためにそうなっていないのだと思います。
> GC が働く前にプロセスが終了してしまう場合などはファイナライザ
> が呼び出されることは保証されていないはずです。
これ、私も必ず呼び出されると思っていたのですが、
実際は呼び出されないことがありました。
OSのプロセスが終了する場合は、OS自身がアンマネージ
リソースを解放できるからだと思っています。
私もC++のようにデストラクタを活用したい口なので、
しかたなくusing書きまくってます。
> いや、Javaのときも感じたんだけど、
> 「ガベコレあるから散らかしっぱなしにできて楽ちん」
> ってのは"うそっぱち"ですよね? 少なくともメモリ以外のリソース
> を使ったんならプログラマが責任もってお片づけしたらんと。
禿同禿同 :-)
結局、ちゃんとコントロールすることが大事だし、
そうせざるを得ないです。
ちなみに、2.0ではまた調べてませんが、ADO.NETの実装で
DBへの接続のClose()とDispose()は意味合いが異なるので
注意が必要です。
#どうせならICloseableを作ったほうが良かったのではと。
----------------------
Kouji Suzuki / Microsoft Certified Professional
mailto:k@...
# 実行形式の添付ファイルかHTMLメールは自動的に廃棄されます。
# It will be automatically discarded if the attached file of
# executable files or "HTML mail" is transmitted to this mail address.
> C#にはあるみたいやけど > デストラクタ
C#のデストラクタは構文上はC++のデストラクタと同じですが、
コンパイルすると Finalize() になります。
ファイナライザ(とDispose)の実装がJavaと違ってややこしく
感じるのは、アンマネージリソースの制御を支配できるからです。
Dispose()が明示的に呼び出された場合、そのクラス内で暗黙に
(有効な)他のオブジェクトへの参照を保持しているか、
あるいは全く保持していないので、Dispose()の実装は安全に
動作することができます。
しかし、Finalize()内ではもはやクラスで保持している参照は
無効となっている可能性があるため、うかつに操作出来ません。
#なんだったか、そこで無効な参照を操作すると例外が出た
#ような気がしますが未確認。
では、Finalize()では何もできないのかというとそうではなく、
無効な参照さえ使用しなければ、アンマネージリソースの解放が
出来ます。
たとえば、IntPtr型で保持した値は参照ではなく値型なので
これを使って解放できます。
#ファイルハンドル類とかメモリのハンドルはIntPtrに入って
#いるので、CloseHandle() みたいなことをすればよい。
この問題とDispose()の実装をうまくするために、プログラミング
..NET Framework P.475で示されるような実装を行うことを
推奨しています。
Dispose()は明示的に呼び出さない限りは、GCが呼び出すことは
ありません(C#ではusingのブロックを出るときにDispose()を
呼び出すfinalizeを生成できるけど同じです)。
GCがIDisposableを実装していることを検知して、自動的に
Dispose()を呼び出してくれれば良いのに、と思ったことも
ありますが、以上のような理由で
「そのタイミングでDispose()が呼ばれても困ったチャンになる」
ためにそうなっていないのだと思います。
> GC が働く前にプロセスが終了してしまう場合などはファイナライザ
> が呼び出されることは保証されていないはずです。
これ、私も必ず呼び出されると思っていたのですが、
実際は呼び出されないことがありました。
OSのプロセスが終了する場合は、OS自身がアンマネージ
リソースを解放できるからだと思っています。
私もC++のようにデストラクタを活用したい口なので、
しかたなくusing書きまくってます。
> いや、Javaのときも感じたんだけど、
> 「ガベコレあるから散らかしっぱなしにできて楽ちん」
> ってのは"うそっぱち"ですよね? 少なくともメモリ以外のリソース
> を使ったんならプログラマが責任もってお片づけしたらんと。
禿同禿同 :-)
結局、ちゃんとコントロールすることが大事だし、
そうせざるを得ないです。
ちなみに、2.0ではまた調べてませんが、ADO.NETの実装で
DBへの接続のClose()とDispose()は意味合いが異なるので
注意が必要です。
#どうせならICloseableを作ったほうが良かったのではと。
----------------------
Kouji Suzuki / Microsoft Certified Professional
mailto:k@...
# 実行形式の添付ファイルかHTMLメールは自動的に廃棄されます。
# It will be automatically discarded if the attached file of
# executable files or "HTML mail" is transmitted to this mail address.
▼ スレッド
- 314: すんません、基本的なこと確認させてくださいませ。 C#では(.NETならなんでもいいけど)、メモ FUKUDA, Fumiki
- ├315: finally で明示的に Close() を呼ぶか、using を使う必要があった と思います。 GC があるけど、デス Imabeppu
- │└316: んむ。using(...) はデストラクタがちゃんと後始末(Close)して くれてれば、って但し書きがつくん FUKUDA, Fumiki
- │ ├318: Dispose ですね。 あるにはあるんですけど、using 使わないと呼び出されるタイミング が C++ と違 Imabeppu
- │ │├320: そかそか「スコープ外れたら直ちに起動」じゃないか。 「GCのついでに」なんだな。 ヘタすり FUKUDA, Fumiki
- │ │└327: アレはデストラクタと呼ばれているけど実体はファイナライザです。 GCが開放するときにしか Tietew
- │ └328: C#のデストラクタは構文上はC++のデストラクタと同じですが、 コンパイルすると Finalize() にな Kouji Suzuki
- │ └329: 訂正。 上記はデバッガで確認したもので、実際にはFinalize()で発生した 例外はGCでにぎりつぶさ Kouji Suzuki
- │ └330: まとめてみる。間違いあったら突っ込んでおくんなさい。 auto: スコープから外れたらデストラ FUKUDA, Fumiki
- │ ├331: heapの場合、GCされるのを待たずとも明示的にdeleteすることで デストラクタを確実に呼び出すこ FUKUDA, Fumiki
- │ ├332: C++/CLIでは、~X()はIDispose::Dispose()と、!X()はFinalize()と(ほぼ)同義で す(「ほぼ」と書いたのは親ク Satoshi Nakamura
- │ │└333: 子::!子()されたとき、親::!親()は呼ばれないってことですか? それともC#では子.Finalize()時に親.Fil FUKUDA, Fumiki
- │ └334: auto: スコープから外れたらデストラクタ X::~X() が'必ず'動く。 heap: 明示的にdeleteされたらX::~X() FUKUDA, Fumiki
- └317: 実際には実装依存、、、だっけかな? お作法としては、Disposeにリソースを開放する処理を書い S.Ono
- └319: なんだかなー… なんもかんもusingでくるめって薦めるくらいなら、 FUKUDA, Fumiki
- └321: automatic 変数を許すとして、どんな書き方になるんでしょうね。 特別な書き方をするなら、using Imabeppu
- └322: んむ。 C++/CLIだと: Sister^ one = gcnew Sister("恭子"); // GC-heap Sister two("美香"); // auto(stack) なんすけど FUKUDA, Fumiki
- └323: 参照してくれてる人がいるかどうかの確認なら楽 (っつ〜か参照カウン タ使えば一瞬) なんだけ Takao Ono
- └324: デストラクタ(というかファイナライザというか)は必ずしも呼び出されることが 保障されてな S.Ono
- └325: 遅くとも GC のタイミングで必ず呼出されることが保証されてます. こっちは using を使えば本体 Takao Ono
- └326: GC が働く前にプロセスが終了してしまう場合などはファイナライザ が呼び出されることは保証 Shinichi Aoyagi