AppDomain.Unload(myDomain) を実行すると、完全なガベージ コレクションも実行されると予想されます。
「C# による CLR」の Jeffrey Richter によると、彼は AppDomain.Unload 中に次のように述べています。
CLR はガベージ コレクションを強制的に発生させ、現在アンロードされている AppDomain によって作成されたオブジェクトによって使用されていたメモリを再利用します。これらのオブジェクトの Finalize メソッドが呼び出され、オブジェクトが適切にクリーンアップされる機会が与えられます。
「.NET Framework 共通言語ランタイムのカスタマイズ」の「Steven Pratschner」によると:
すべてのファイナライザーが実行され、ドメインで実行中のスレッドがなくなると、CLR は内部実装で使用されるすべてのメモリ内データ構造をアンロードする準備が整います。ただし、これが発生する前に、ドメインに存在するオブジェクトを収集する必要があります。次のガベージ コレクションが発生した後、アプリケーション ドメインのデータ構造はプロセス アドレス空間からアンロードされ、ドメインはアンロードされたと見なされます。
私は彼らの言葉を誤解していますか?予期しない動作を再現するために次の解決策を実行しました(.net 2.0 sp2で):
このインターフェイスを含む「Interfaces」という名前のクラス ライブラリ プロジェクト:
public interface IXmlClass
{
void AllocateMemory(int size);
void Collect();
}
「Interfaces」を参照し、次のクラスを含む「ClassLibrary1」という名前のクラス ライブラリ プロジェクト:
public class XmlClass : MarshalByRefObject, IXmlClass
{
private byte[] b;
public void AllocateMemory(int size)
{
this.b = new byte[size];
}
public void Collect()
{
Console.WriteLine("Call explicit GC.Collect() in " + AppDomain.CurrentDomain.FriendlyName + " Collect() method");
GC.Collect();
Console.WriteLine("Number of collections: Gen0:{0} Gen1:{1} Gen2:{2}", GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2));
}
~XmlClass()
{
Console.WriteLine("Finalizing in AppDomain {0}", AppDomain.CurrentDomain.FriendlyName);
}
}
「Interfaces」プロジェクトを参照し、次のロジックを実行するコンソール アプリケーション プロジェクト:
static void Main(string[] args)
{
AssemblyName an = AssemblyName.GetAssemblyName("ClassLibrary1.dll");
AppDomain appDomain2 = AppDomain.CreateDomain("MyDomain", null, AppDomain.CurrentDomain.SetupInformation);
IXmlClass c1 = (IXmlClass)appDomain2.CreateInstanceAndUnwrap(an.FullName, "ClassLibrary1.XmlClass");
Console.WriteLine("Loaded Domain {0}", appDomain2.FriendlyName);
int tenmb = 1024 * 10000;
c1.AllocateMemory(tenmb);
Console.WriteLine("Number of collections: Gen0:{0} Gen1:{1} Gen2:{2}", GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2));
c1.Collect();
Console.WriteLine("Unloaded Domain{0}", appDomain2.FriendlyName);
AppDomain.Unload(appDomain2);
Console.WriteLine("Number of collections after unloading appdomain: Gen0:{0} Gen1:{1} Gen2:{2}", GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2));
Console.WriteLine("Perform explicit GC.Collect() in Default Domain");
GC.Collect();
Console.WriteLine("Number of collections: Gen0:{0} Gen1:{1} Gen2:{2}", GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2));
Console.ReadKey();
}
コンソール アプリケーションを実行したときの出力は次のとおりです。
Loaded Domain MyDomain
Number of collections: Gen0:0 Gen1:0 Gen2:0
Call explicit GC.Collect() in MyDomain Collect() method
Number of collections: Gen0:1 Gen1:1 Gen2:1
Unloaded Domain MyDomain
Finalizing in AppDomain MyDomain
Number of collections after unloading appdomain: Gen0:1 Gen1:1 Gen2:1
Perform explicit GC.Collect() in Default Domain
Number of collections: Gen0:2 Gen1:2 Gen2:2
注意事項:
ガベージ コレクションはプロセスごとに行われます (単なる復習)
アンロードされる appdomain 内のオブジェクトにはファイナライザーが呼び出されますが、ガベージ コレクションは行われません。AllocateMemory() によって作成された 10 メガバイトのオブジェクトは、上記の例で明示的な GC.Collect() を実行した後にのみ収集されます (またはガベージ コレクターが後で収集する場合)。
その他の注意事項: XmlClass がファイナライズ可能かどうかは問題ではありません。上記の例でも同じ動作が発生します。
質問:
AppDomain.Unload を呼び出してもガベージ コレクションが発生しないのはなぜですか? その呼び出しをガベージ コレクションにする方法はありますか?
AllocateMemory() 内で、LargeObject ヒープ上に生成され、第 2 世代のオブジェクトとなる短期間の大きな xml ドキュメント (16 MB 以下) をロードする予定です。明示的な GC.Collect() またはガベージ コレクターの他の種類の明示的なプログラムによる制御に頼らずにメモリを収集する方法はありますか?