【发布时间】:2011-02-12 23:51:01
【问题描述】:
当我执行 AppDomain.Unload(myDomain) 时,我希望它也会进行完整的垃圾回收。
根据 Jeffrey Richter 在“CLR via C#”中的说法,他在 AppDomain.Unload 期间说:
CLR 强制进行垃圾回收,回收所有对象使用的内存 由现在卸载的 AppDomain 创建的。 Finalize 方法 这些对象被调用,让对象有机会正确清理自己。
根据“自定义 .NET Framework 公共语言运行时”中的“Steven Pratschner”:
在所有终结器都运行并且域中没有更多线程在执行之后,CLR 准备好卸载内部实现中使用的所有内存数据结构。然而,在此之前,必须收集驻留在域中的对象。下一次垃圾回收发生后,应用程序域数据结构将从进程地址空间中卸载,并且该域被视为已卸载。
我误解了他们的话吗? 我做了以下解决方案来重现意外行为(在 .net 2.0 sp2 中):
一个名为“Interfaces”的类库项目包含这个接口:
public interface IXmlClass
{
void AllocateMemory(int size);
void Collect();
}
一个名为“ClassLibrary1”的类库项目,它引用“Interfaces”并包含此类:
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);
}
}
引用“接口”项目并执行以下逻辑的控制台应用程序项目:
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() 或其他类型的垃圾收集器显式编程控制的情况下收集内存?
【问题讨论】:
标签: c# applicationdomain