オブジェクトのメモリは、そのオブジェクトがアクセス可能である限り、回収されることはない。オブジェクトがアクセス不可能になるとオブジェクトのメモリはGCの対象となり、適切なタイミングでGCが回収してくれる。
変数(フィールドやらローカル変数やら)に格納されている参照のことを「強い参照」と言う。
「アクセス可能なオブジェクト」とは、強い参照が存在するオブジェクトのことを言う。ただし、その強い参照を所持しているオブジェクトもアクセス可能なオブジェクトである必要がある。つまり、強い参照を所持しているオブジェクトを辿って行くとアクセス不可能なオブジェクトに辿り着く場合、それらは全てアクセス不可能なオブジェクトである。また、実際にアクセスされている最中(コンストラクタ実行中だったりメソッド実行中だったり)のオブジェクトも、アクセス可能なオブジェクトである。
冒頭でも書いたとおり、アクセス可能なオブジェクトはGCの対象にはならない。
しかし、「強い参照」が存在せず「弱い参照」が存在するオブジェクトなら、弱い参照を通して呼び出すことができる上、GCの対象となる。
「弱い参照」とは、WeakReferenceクラスによる参照である。WeakReferenceは、コンストラクタで対象オブジェクトを受け取る。WeakReferenceオブジェクトのTargetプロパティを通して対象オブジェクトにアクセスできるのだが、WeakReferenceオブジェクトは、内部に強い参照を保持するのではなく、参照をIntPtr構造体として保持している。これにより、対象オブジェクトはGCの対象となる。
対象オブジェクトがGCの対象となるので、GCによって対象オブジェクトのメモリが回収された場合は、Targetプロパティはnull参照を返す。そのため、対象オブジェクトにアクセスする際は、Targetプロパティから強い参照を取得して、強い参照を通してアクセスする必要がある。
具体的には、まずTargetプロパティを変数に代入して強い参照を取得する。そして、その変数の値がnull参照であった場合は、再び対象オブジェクトを生成する。その後、その変数を通して対象オブジェクトにアクセスし、最後に強い参照を消去する(変数にnull参照を代入したり、ローカル変数ならスコープ外に処理が移れば勝手に消える)。
弱い参照の作成・使用の例
class Piyo
{
// Hogeオブジェクトへの弱い参照
private WeakReference weakHoge;
// コンストラクタ
public Piyo()
{
Hoge hoge = this.CreateHoge();
this.weakHoge = new WeakReference(hoge);
hoge = null;
}
// Hogeオブジェクトを使うメソッド
public void UseHoge()
{
Hoge hoge = (Hoge)this.weakHoge.Target;
if (hoge == null)
{
hoge = this.CreateHoge();
this.weakHoge.Target = hoge;
}
//
// Hogeを使った処理
//
hoge = null;
}
// Hogeオブジェクトを生成するメソッド
private Hoge CreateHoge()
{
Hoge hoge = new Hoge();
hoge.Name = "ほげ"
return hoge;
}
}
IsAliveプロパティを使って、対象オブジェクトがGCに回収されているかどうかを調べたり、直接Targetプロパティとnullを比較することもできるが、これらのプロパティにアクセスした時は回収されていなくても、直後に回収されてしまうというケースもありえる。なので、上記の例のように最初にTargetプロパティから強い参照を取得する必要がある。(強い参照の取得さえちゃんとすれば、IsAliveやTargetとの比較を用いても問題ない。)
弱い参照を使用するシーンとしては、サイズの大きなオブジェクトを保持して利用する際に、そのオブジェクトが常にメモリを占有してしまうよりは、GCの対象としておき、回収された場合は再び作成するという手段を取った方が、作成にかかるコストを考慮しても有益だという場合などである。