gpt4 book ai didi

c# - 为什么调用 AppDomain.Unload 不会导致垃圾回收?

转载 作者:IT王子 更新时间:2023-10-29 04:43:50 24 4
gpt4 key购买 nike

当我执行 AppDomain.Unload(myDomain) 时,我希望它也执行完整的垃圾收集。

根据 Jeffrey Richter 在“CLR via C#”中的说法,他说在 AppDomain.Unload 期间:

The CLR forces a garbage collection to occur, reclaiming the memory used by any objects that were created by the now unloaded AppDomain. The Finalize methods for these objects are called, giving the objects a chance to clean themselves up properly.

根据“自定义 .NET Framework 公共(public)语言运行时”中的“Steven Pratschner”:

After all finalizers have run and no more threads are executing in the domain, the CLR is ready to unload all the in-memory data structures used in the internal implementation. Before this happens, however, the objects that resided in the domain must be collected. After the next garbage collection occurs, the application domain data structures are unloaded from the process address space and the domain is considered unloaded.

我是不是误解了他们的话?我做了以下解决方案来重现意外行为(在 .net 2.0 sp2 中):

包含此接口(interface)的名为“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);
}
}

引用“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

注意事项:

  1. 垃圾收集是按进程完成的(只是回顾一下)

  2. 卸载的应用程序域中的对象调用了终结器,但未完成垃圾回收。由 AllocateMemory() 创建的 10 MB 对象只会在上例中执行显式 GC.Collect() 后被收集(或者如果垃圾收集器稍后会收集。

其他说明:XmlClass 是否可终结并不重要。上面的示例中发生了相同的行为。

问题:

  1. 为什么调用 AppDomain.Unload 不会导致垃圾回收?有什么方法可以使该调用产生垃圾回收?

  2. 在 AllocateMemory() 内部,我计划加载短暂的大型 xml 文档(小于或等于 16 mb),这些文档将进入 LargeObject 堆并将成为第 2 代对象。有没有什么方法可以在不诉诸显式 GC.Collect() 或其他类型的垃圾收集器显式编程控制的情况下收集内存?

最佳答案

附加说明:

在与 Jeffrey Richter 进行了一些邮件交流后,他非常友好地看了这个问题:

OK, I read your post.
First, the array will not be GC’d until the XMLClass object is GC’d and it takes TWO GCs to collect this object because it contains a Finalize method.
Second, unloading an appdomain at least performs the marking phase of the GC since this is the only way to determine which objects are unreachable so that their Finalize methods can be called.
However, the compact part of the GC might or might not be done when unloading a GC.Calling GC.CollectionCount obvious does not tell the whole story. It is not showing that the GC marking phase did occur.
And, it’s possible that AppDomain.Unload starts a GC via some internal code which does not cause the collection count variables to be incremented. We already know for a fact that the marking phase is being performed and that collection count is not reflecting this.

A better test would be to look at some object addresses in the debugger and see if compaction actually occurs. If it does (and I suspect it does), then the collection count is just not being updated correctly.

If you want to post this to the web site as my response, you can.

在听取了他的建议并研究了 SOS(也删除了终结器)后,它揭示了这一点:

在 AppDomain.Unload 之前:

!EEHeap -gc
Number of GC Heaps: 1
generation 0 starts at 0x0180b1f0
generation 1 starts at 0x017d100c
generation 2 starts at 0x017d1000
ephemeral segment allocation context: none
segment begin allocated size
017d0000 017d1000 01811ff4 0x00040ff4(266228)
Large object heap starts at 0x027d1000
segment begin allocated size
027d0000 027d1000 02f75470 0x007a4470(8012912)
Total Size 0x7e5464(8279140)
------------------------------
GC Heap Size 0x7e5464(8279140)

在 AppDomain.Unload 之后(相同的地址,没有进行堆压缩)

!EEHeap -gc
Number of GC Heaps: 1
generation 0 starts at 0x0180b1f0
generation 1 starts at 0x017d100c
generation 2 starts at 0x017d1000
ephemeral segment allocation context: none
segment begin allocated size
017d0000 017d1000 01811ff4 0x00040ff4(266228)
Large object heap starts at 0x027d1000
segment begin allocated size
027d0000 027d1000 02f75470 0x007a4470(8012912)
Total Size 0x7e5464(8279140)
------------------------------
GC Heap Size 0x7e5464(8279140)

在 GC.Collect() 之后,地址不同表明堆压缩已经完成。

!EEHeap -gc
Number of GC Heaps: 1
generation 0 starts at 0x01811234
generation 1 starts at 0x0180b1f0
generation 2 starts at 0x017d1000
ephemeral segment allocation context: none
segment begin allocated size
017d0000 017d1000 01811ff4 0x00040ff4(266228)
Large object heap starts at 0x027d1000
segment begin allocated size
027d0000 027d1000 027d3240 0x00002240(8768)
Total Size 0x43234(274996)
------------------------------
GC Heap Size 0x43234(274996)

经过更多 sos 之后,我得出的结论是,这肯定是设计使然,堆压缩不一定完成。在 AppDomain 卸载期间,您真正可以确定的唯一一件事是对象将被标记为不可访问,并将在下一次垃圾收集期间被收集(就像我说的那样,卸载应用程序域时并没有完全完成,除非有巧合)。

编辑:我还问过直接在 GC 团队工作的 Maoni Stephens。您可以在评论中的某处阅读她的回复 here .她确认这是设计使然。结案:)

关于c# - 为什么调用 AppDomain.Unload 不会导致垃圾回收?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2723579/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com