dispose()は必要か?



 2007-06-13の記事でnowakay氏の”getGraphics/disposeは不要”というコメントに対して2007-07-24

nori 『 8/5まで更新停止とは残念です。はやく試験が終わるといいですね。ところで、過去の記事を読んで非常に気になったことがあるんですが…。それは 6/13の記事のコメントでnowokayさんがdispose()は不要って書いてあることです。僕はdispose()を多用してるので、そこんとこが非常に気になります。SunのJava APIのソースやSDKに含まれているdemoでは普通に使われているみたいなんですが…。』 (2007/07/26 02:00)


nowokay 『disposeが不要ではなくて、画面描画のためにgetGraphics/disposeすることはないという話です。dispose不要という話ではないです。』 (2007/07/27 19:51)


nowokay 『getGraphics/disposeで画面描画すると、ウィンドウが隠れて再び現れたときには再描画されないので、ほとんどの場合問題になります。』 (2007/07/27 19:53)


nori 『 nowokayさんはじめましてこんにちは。お返事ありがとうございます。すべての場面においてgetGraphics()/dispose()が不要というわけではなく、あの(6/13の)サンプルでは使うべきではないということだったんですね。納得しました。
 しかしJavaは面白いけど難しいっすね…精進します。』 (2007/07/28 02:15)

 というコメントが付いた。


 noriさんようこそ!


 さて、dispose()についてはけっこう皆が悩んでいるようで「dispose 必要」でググると出るわ出るわ。

20件ほど斜め読みしたところ、リソースには「マネージド(管理された)リソース」と「アンマネージドリソース」の二種類があるらしく、

確かに、マイクロソフトから提供されているクラスについては、GC がマネージドリソースを回収するときに、アンマネージドリソースの解放も行われます(「回収」と「解放」の言葉を使い分けていることに注意)。しかし、それは、そのように実装されているからです。自分で実装するクラスについては、そのように実装しなければ、GC が動作するタイミングでも解放してもらえません。

 と言うことらしい。また、

最終的には必ず解放されるようにするためには、ファイナライザで解放を指示する必要があります。ファイナライザでは、無条件で Dispose を呼び出すようにします。その為、Dispose は、何度呼ばれても支障がないような書き方をする必要があります。しかし、ファイナライザが実行されるのは、GC による回収が行われるときです。GC による回収は、マネージドリソースが足りなくなりそうなときに行われます。言い方を変えれば、アンマネージドリソースが足りなくなっても、回収はされません。このことから、アンマネージドリソースが不足する、リークしている状態となります。

 だそうな。つまり、ガベージコレクタは完全には信用できず、結論としては”アンマネージドリソースを扱うプログラマーは自分でリソースの開放を行わなければならない””dispose()はファイナライザで実行する”ようだ。


 どの操作でdispose()を指定するべきか?つまりどの命令がアンマネージドリソースを扱っているのか?は「IDisposable」という命令が関係しているようだが、これ以上調べるのは時間がかかりそうなのでここで一旦中止。後日再調査することにする。
↑2007年7月31日線引き




 nowakayさんいらっしゃいませ!解説感謝致します。
 へぇー、成る程。


 早速実験。2007-06-13の冒頭のサンプルソースでは

 となった。一旦ウインドウ内で円を描いてから別のウインドウをかぶせて、どけると、ああ確かに再描画されないですな。


 では、サンプルソースからdispose()を削除して再実行。

 んん〜?同じ様に見えるが・・・。サンプルがいけないんかな・・・?


 後日追記:(2007年7月31日)
 上記の引用記事は.NETでの議論であった。つまりJavaではないので削除する。
 再度検索し以下のような記事を発見。
じゃばじゃばのJavaTIPSの中から無断転載。

最適化/システムリソースの解放
【前略】
Javaの場合にはこれらのシステムリソースをプログラムから直接操作することはありません。システムリソースを使用しているオブジェクトに対する操作により間接的に制御することになります。
java.lang.Objectにはfinalizeという資源解放を目的としたメソッドが用意されています。システムリソースを操作するオブジェクトはこのfinalizeメソッドをオーバライドし、システムリソースの解放処理をインプリメントします。ガベージコレクタはオブジェクトを解放する時にこのfinalizeメソッドを呼び出すので、ガベージコレクタによってオブジェクトが解放されると関連するシステムリソースも解放されるというわけです。

ではこのfinalizeメソッドを使用すれば、プログラムから自由自在にシステムリソースの解放が行えるのかというとそういうわけではありません。
finalizeメソッドはあくまでもガベージコレクタから呼ばれることを前提としたメソッドであり、一般のクラスから呼び出すことはできないようになっています。

つまりJavaでは普通にプログラムを作成すると、システムリソースを使用しているオブジェクトを使用してすぐに廃棄してもガベージコレクションによって回収されるまでシステムリソースは解放されないのです。
これはかなり重大な問題といわざるをえないでしょう。
【後略】

 また、同じページの最後の他のTIPSへのリンクがあるがその内の下の4つ

にも注目すべし。
 色々細かく書いてくれていて嬉しい。


 ただ、どうもよく分らないことがある。 
 この記事に限らないのだが、ダブルバッファのシステムリソースの解放をやってるのは分るが、ダブルバッファと言うからにはアニメーションするために、繰り返し何度も何度もこのリソース(bufferGというグラフィクスコンテキストとbufferImageというイメージ)を使うわけだ。だから、一々「取得して開放して」を繰り返さなくても、使う前に取得して、{繰り返し使用し}使い終わった後に開放してやればいい、と思うのだが。ソース書きの手間はともかく、毎回「取得開放」を実行するとしたら取得するためには(前に開放=消しちゃったから)毎回新たにメモリエリアを設定し直さなければならない。それだけCPUもムダに使うハメになるのではないだろうか?