.NET GC & Interop クイズ
.NET GC & Interop クイズ解答編 より
最初に見たときは 「えー、なんでー?」 と思ったんですが、、、
トラックバックをたどってみると 菊池さん、NyaRuRu さん が Yes、Ladybug さん が No。うーん。。。
で、菊池さんのところからリンクされてる Lifetime, GC.KeepAlive, handle recycling を読んで納得。JIT にとっては言語上のブロックとかそういったものはまったく関係ないわけで、this._handle にアクセスし終わった時点で 「もうこの this は不要」 と判断しても構わないわけで、そうなるとファイナライザが呼ばれることだってあるわけで、そうすると WorkOnHandle メソッドを呼び出す時点では this._handle が変わっちゃってることだってあると。そういうことですよね。
で、これは WorkOnHandle メソッドが static でなく、Wrapper クラスのインスタンスメソッドならこういうことは起こらない(メソッドを呼ぶこと自体に this が必要だから) と。たぶん Ladybug さんが言ってるのはそういうことですよね?
ということは、こういうことに注意しなくちゃいけない条件としては、
- あるクラスのインスタンスメソッド(これを m メソッドとする)の中で static メソッドを呼び出す。
- その static メソッドの引数に何らかのフィールド(例: this._handle)を渡している。
- その引数に渡しているフィールドはファイナライザで何らかの操作が行なわれる可能性がある。
- その static メソッドの呼び出し以降に this にアクセスするコードが一切ない。
- (m メソッドを呼び出す側で、m メソッドの呼び出し以降にこの 「あるクラス」 にアクセスするコードがあれば GC されないからこういうことはおきない。が、普通は 「どう使われるか」 なんてことは事前にわからないので条件に含めることはできない)
と言う場合は GC.KeepAlive() を使って this を守ってあげる必要があるということでいいのかな?
static かは本題ではないですね
返信削除同じことが起こっても納得できる例
Repro Steps:
class Hoge
{
ArrayList list;
~Hoge()
{
list.RemoveAll()
}
}
で
Hoge hoge = new Hoge();
ArrayList list = hoge.list;
list.Add( x );
list.Add( y );
hoge = null;
// Hoge の本来の寿命はここまで
// 以下では Hoge を触っていないというか触れない
GC.Correct(); // ここでGCがおきたとしよう。 ~Hoge が GCで呼ばれるよね
Console.WriteLine( list.Count ); // 0 って出るよね。
ってことだね。
って言っても、このコードには問題があって、ファイナライザでは他のマネージオブジェクトを触っちゃ駄目って決まりを犯してるのよね。
これを守る限りはマネージで再現させる方法は無いと思います。