当我执行一个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