クラスのフィールド(メンバー変数)の挙動に関する質問となります。
あるAというクラスがあり、
AのフィールドにはBという構造体(フィールド名BB)、Cというクラス(フィールド名CC)があります。
Aのクラスをnewして使っており(newしたインスタンスへの参照を変数AAに入れているとします)
使い終わったので、変数AAにnullを代入し、確保したインスタンスを解放(厳密にはGCのコレクション対象にする)した場合、
AのフィールドであるフィールドBBはAAが実際にGCにコレクションされた際に解放され、
フィールドCCは、GCにコレクションされた際にはCCにnullをセットしていないので解放されないのでしょうか?
それとも参照型のフィールドに関しては、nullをセットしなくても自動的にnullがセットされた状態?になるか、
または強制的に解放されるような仕組みでしょうか?
自分でファイナライザで参照型のフィールドにはnullをセットしなければいけないのかが
ネットで検索しても上手くヒットしなかった為、質問させて頂きました。
宜しくお願いします。
Dispose パターンというのをご存じですか? それを実装しなければならないのはどういうケースかご存じですか?
そのあたりが分かっていればこのスレッドの質問はしなくてすんだと思います。
そもそもnullをセットすること自体が蛇足です。
>フィールドCCは、GCにコレクションされた際にはCCにnullをセットしていないので解放されないのでしょうか?
マイクロソフトの資料だと、gentaroさんのリンクの詳細にあたる
https://docs.microsoft.com/ja-jp/dotnet/standard/garbage-collection/fundamentals#fundamentals-of-memory
この辺が相当しています。
以下、上記資料とガーベッジコレクションという言葉からの類推ですが、GCの基本はマークアンドスイープだと思うので
https://ja.wikipedia.org/wiki/%E3%83%9E%E3%83%BC%E3%82%AF%E3%83%BB%E3%82%A2%E3%83%B3%E3%83%89%E3%83%BB%E3%82%B9%E3%82%A4%E3%83%BC%E3%83%97
AAがコレクトされたなら、AAから参照されているCC以外に、有効なスレッド/タスクから辿れるCCを参照している人がいなければCCもコレクトされると思います。
そこまでしてる人はなかなかいないと思いますが、ソースとかはこの辺にあるので、厳密に知りたければそちらを辿って読んでください。
https://referencesource.microsoft.com/#mscorlib/system/gc.cs
またDisposeはリソースの話なのでメモリとは関係ありません。
リソースの開放はGCにコレクトされた後のファイナライズをのんびり待てるほど余裕がないものも多いので、そのために別途用意された機構がDisposeだと思っています。
CC は AA への参照が無くなった時点でルートからたどれなくなるのでガベージコレクション対象になります。null 代入は不要です。AA と CC のどちらが先に解放されるかは決まっていません。BB については構造体なのでガベージコレクションされません。AA のメモリの一部なので AA がコレクトされてメモリが解放されることが BB の解放になります。
> またDisposeはリソースの話なのでメモリとは関係ありません。
違いますね。
> リソースの開放はGCにコレクトされた後のファイナライズをのんびり待てるほど余裕がないものも多いので、そのために別途用意された機構がDisposeだと思っています。
これも違います。
皆さんからのご返信にすごく助かっております。ありがとうございます。
>CC は AA への参照が無くなった時点でルートからたどれなくなるのでガベージコレクション対象になります。null 代入は不要です。
上記はCCをAAから取得し、外部の誰かが参照していた場合も考えられるかと思いますが、その場合はAAにnullをセットしたら、内部挙動としては、AA内のCCにはnullがセットされるような形になり、外部の誰かが参照している変数にnullをセットされるまではCCの実体は解放されないという挙動で合っておりますか?
> またDisposeはリソースの話なのでメモリとは関係ありません。
これは、ファイルオープン系とかのネットワーク係のリソースでは自動的にクローズされないので、メモリとは関係ないというような意味で合っておりますか?
宜しくお願いします。
> という挙動で合っておりますか?
合っていません。null の代入は忘れてください。hihijiji さんのおっしゃる通り、蛇足でしかありません。
他から参照されているのであればガベージコレクションの対象にはならないので解放もされません。null を代入しようがすまいが同じです。内部で代入されるということもありません。
@osaka-umeda
> これは、ファイルオープン系とかのネットワーク係のリソースでは自動的にクローズされないので、メモリとは関係ないというような意味で合っておりますか?
合ってますよ。SurferOnWwwさんが言いたいのはアンマネージドリソースだから違うとかアンマネージドなメモリとかが違うとか駄々をこねてるだけだと思います。その証拠に理由を書いてないですからね。
マネージリソースだってアンマネージドリソースを使います。マネージリソースがcloseされずに放置されてたなら、システムやフレームがどう頑張ってもアンマネージドリソースを開放することができない=マネージリソースの開放も目的にしてるのですよ。
皆さんご返信ありがとうございます。
> 合っていません。null の代入は忘れてください。hihijiji さんのおっしゃる通り、蛇足でしかありません。
> 他から参照されているのであればガベージコレクションの対象にはならないので解放もされません。null を代入しようがすまいが同じです。内部で代入されるということもありません。
C#では、nullを自分でセットするようなことは全般的にしなくて大丈夫ということでしょうか?
メソッド内のローカル変数じゃない場合は、明示的に使わなくなったフィールドに関してはnullをセットしてあげないといつまでも保持してしまうので、必要なくなったフィールド(参照型)にはnullをセットして使っていないようにしないといけない(参照カウンターをゼロにする)と思いますが、
フィールドじゃなく、そのフィールドを保持しているクラスの変数自体は、そのクラスの変数にnullをセットしてあげれば、
内部のフィールドに関しては、特にnullをセットしなくてよいという意味で合っておりますか?
> マネージリソースだってアンマネージドリソースを使います。マネージリソースがcloseされずに放置されてたなら、システムやフレームがどう頑張ってもアンマネージドリソースを開放することができない=マネージリソースの開放も目的にしてるのですよ。
なるほど、理解出来ました。ありがとうございます。
参照カウンターは使われていません。もっと賢い方法で判定しています。リンクされたページを詳しく読んでみると書いてあるんじゃないでしょうか。
> C#では、nullを自分でセットするようなことは全般的にしなくて大丈夫ということでしょうか?
個人的な感覚で言えば大丈夫です。
nullにしてあげればGCがマークアンドスイープする際にトラバースする区間が減って楽をするケースはあるかもしれません。時代的にはnullを使用しない流行りのようなものもありますし、親切心からnullにして、NullPointerExceptionが起きてお前のせいだ~となるケースだってあるかもですね。
基本nullを使う設計なら、個人的にお作法として使用しないときはnullが良いとは思います。
> マネージリソースだってアンマネージドリソースを使います。マネージリソースがcloseされずに放置されてたなら、システムやフレームがどう頑張ってもアンマネージドリソースを開放することができない=マネージリソースの開放も目的にしてるのですよ。
そういうの初耳。先のスレッドで Microsoft の公式文書の裏付けがないとレスは意味がないというようなことを言っていましたよね? どの文書のどこにそういうことが書いてあるか教えてくださいな。
質問者さん>
.NET アプリでリソースの開放には Dispose パターンを実装します。ただし、
アンマネージドリソース ⇒ GC で解放できない ⇒ Dispose パターン利用・・・ということです。
裏を返せば、アンマネージドリソースを使ってなければ Dispose パターンの実装は不要 ⇒ 今回の質問のマネージドリソースは GC 任せでよい(null をセットとかは必要ない)ということです。
ということで、最初の私のコメント、
> Dispose パターンというのをご存じですか? それを実装しなければならないのはどういうケースかご存じですか?
・・・に対する答えが Yes であれば、今回の質問は出てこないはず。
なお、Dispose パターンの実装はアンマネージドリソースで使用されるメモリを解放する目的もあるわけで、メモリに関係ないということではありません。
> 基本nullを使う設計なら、個人的にお作法として使用しないときはnullが良いとは思います。
あなたが重視している Microsoft の公式文書の裏付けはあるのでしょうか? あなたが言うには Microsoft の公式文書の裏付けがないと意味がなく、個人的に「良いとは思う」なんてのは問題外だったのでは?
色々とご説明、みなさんありがとうございます。
アドバイス頂いたMSのガベージコレクションの説明ページを読んでいて、1点気になったのが、nullをセットしないで問題ないと皆さんからのご返信がありましたが、もし、シングルトンパターンで、マネージャーのようなクラスが常に存在しており、処理するオブジェクトを管理していた場合は(このオブジェクト群はこのマネージャーしか参照していない)、そのオブジェクトをGCの対象にする為にもnullがセットされていないといつまでも参照されているので解放されないと思うのですが(勝手にGCされてしまったら、マネージャーがもしどこかのタイミングでそのオブジェクトを実行しようとした時に勝手に解放されていたら困るので)、この認識は間違いでしょうか?
シングルトンはアプリの終了まで生き続けるオブジェクトなので、終了処理は考えなくて大丈夫です。
すみません、シングルトンのクラスが各オブジェクトを管理しているような場合となります。
例えばですが、UIパーツ(ボタン等)を管理するようなUIマネージャーがあった場合に各UIパーツをUIマネージャーに登録して管理してもらっているのですが、UIとして必要なくなったパーツ(オブジェクト)に関しては、UIマネージャーが管理してるオブジェクトの配列にnullをセットして、そのUIオブジェクトがもう使われていない形にするという意味となります。
シングルトンの悪い使い方ですが、それはともかく、寿命の短いオブジェクトを破棄するのであれば null を代入するのが手っ取り早いと思いますが、使い終わったとしても別に放っておいて大丈夫です。スマホのようなメモリの小さな機器で巨大なメモリを使うなら話は別ですが、普通それが問題になることはないと思います。どうせ GC などめったに働かないので。
すみません、少し話がそれてしまうのですが、シングルトンの悪い使い方というのが理解できずでして、変な意味では無いのですがUIマネージャーのようなのがシングルトンの場合は、管理しているUIに関して全てを非アクティブに出来たり、管理しているUIの中から何かを探す等、すごく便利でして、またUIのマネージャーが複数存在しても困るのでシングルトンなのですが、悪い使い方となりますか?逆に良い使い方というのが思いつかなかったので確認させていただきました。
その為、メモリも使いっぱなしではなく、使用していないオブジェクト(今回であればUIパーツ)に関してはnullをセットして使っていない形にするのは特に問題もなく、UIマネージャー的にも管理しなくてよくなるパーツになるのと、GCの対象にもなるので、nullセットは良い形かと考えていますが、すごく悪い方法なのかとおもってきてしまいました。
データバインディングを使えば UI を探さなくて済むのでマネージャーは不要です。また、寿命の短い UI をシングルトンという寿命の長いオブジェクトで管理する意味もありません。どうしても探さなければならない時には親から探せばいいだけです。
データバインディングのほうをネットで調べてみますね。
@SurferOnWww
あなたが書かなかった理由を推測して書いただけですよ。あなたの書いた理由でいいのなら、マネージリソースというのを考慮しないでいいので、リソースの開放に使うのがDisposeで今回の件には無関係で終わりです。裏を返す必要などないのですよ。私が別の質問で書いたのは、WCFの使い方を説明するのに、MSのリンクも提示せずに自分のHP紹介だけするのは何事か?という話で、それも今回の件とは無関係です。
@osaka-umeda
話が混迷を極めてますが、シングルトンで静的に確保というのは悪手ではありませんよ。
ただ性格がグローバル変数と同じなので、忌み嫌われる傾向にあります。
プロセスに対して1つであることが役割上望まれているオブジェクトはシングルトンで問題ありません。
ただZuishinさんのおっしゃるとおり.NETでは基本UIはバインディングで構築するのが普通ですね。モデルがメインになり、UIはそれを透過的にに反映させるだけというのが理想形だと思います。UIマネージャが何をするものか分かりませんが、相反するものであれば、あまり一般には好まれないものかもしれません。
> プロセスに対して1つであることが役割上望まれているオブジェクトはシングルトンで問題ありません。
シングルトン自体に罪はありませんが、今回の目的には適しません。「悪い使い方」と書いたのは、
- 寿命の短い UI をシングルトンの中で取り換えながら使っている
- コントロールをフォームやウィンドウから外に出し、グローバルに使えるようにしている
が、主な理由です。
皆さま、諸々ありがとうございます。
これより先は自分で調べて、悩んだ際には再度質問させて頂ければと思います。
本当に助かりました、ありがとうございます。
> あなたが書かなかった理由を推測して書いただけですよ。あなたの書いた理由でいいのなら、マネージリソースというのを考慮しないでいいので、リソースの開放に使うのがDisposeで今回の件には無関係で終わりです。裏を返す必要などないのですよ。
意味不明。少なくとも 2020/08/10 17:13 と 2020/08/10 17:47 の私の質問の答えがない。自分でも何を書いているのか分かってないとか?
> 私が別の質問で書いたのは、WCFの使い方を説明するのに、MSのリンクも提示せずに自分のHP紹介だけするのは何事か?
WCFの使い方を説明? 誰が? そもそもレスのリンク先は読んでないと言ってた気がするけど?
もう一度聞きますが、逃げないで先の私の質問(以下に再掲)に答えてもらえませんか? あなたのレスを質問者さんが信じてしまってますけど。特に「マネージリソースだってアンマネージドリソースを使います」というところは決定的に間違っていて、そこをベースに始まるその後の記述も間違っているのでは?
> マネージリソースだってアンマネージドリソースを使います。マネージリソースがcloseされずに放置されてたなら、システムやフレームがどう頑張ってもアンマネージドリソースを開放することができない=マネージリソースの開放も目的にしてるのですよ。
そういうの初耳。先のスレッドで Microsoft の公式文書の裏付けがないとレスは意味がないというようなことを言っていましたよね? どの文書のどこにそういうことが書いてあるか教えてくださいな。