當我執行一個AppDomain.Unload(myDomain)時,我希望它也做一個完整的垃圾收集。爲什麼調用AppDomain.Unload不會導致垃圾回收?
根據傑弗裏裏希特在「通過C#CLR」他說,一個AppDomain.Unload期間:
的CLR強制垃圾收集發生,回收由創建的任何對象 使用的內存現在卸載的AppDomain。調用這些對象的最終確定方法爲 ,使對象有機會正確清理自己。
據「史蒂芬Pratschner」中的「自定義.NET Framework公共語言運行庫」:
畢竟終結已經運行並沒有更多的線程在域中執行時,CLR準備卸載內部實現中使用的所有內存數據結構。但是,在此之前,必須收集駐留在域中的對象。在發生下一次垃圾回收後,將從進程地址空間卸載應用程序域數據結構,並認爲該域已卸載。
我在誤解他們的話嗎? 我做了以下的解決方案,以重現意外的行爲(在.NET 2.0 SP2):
稱爲「接口」包含此接口的類庫項目:
public interface IXmlClass
{
void AllocateMemory(int size);
void Collect();
}
一個類庫項目名爲「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);
}
}
A中引用的「接口」項目,並執行以下邏輯的控制檯應用程序項目:
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不會導致垃圾回收?有沒有辦法讓這個調用導致垃圾回收?我打算加載短期大型XML文檔(小於或等於16 MB),它將在LargeObject堆上獲得,並且將成爲第2代對象。有沒有什麼辦法可以在不使用顯式GC.Collect()或其他類型的垃圾收集器的顯式程序控制的情況下收集內存?
我完全同意你的看法。 – Steven 2010-04-27 18:21:10
這可能是由設計,請參閱我的附加說明添加到問題。 – 2010-04-29 07:18:14