本文介绍了为什么呼叫AppDomain.Unload不会导致垃圾收集?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我通过C#CLR在执行AppDomain.Unload(myDomain的),我希望它也做了充分的垃圾收集。



据杰弗里里希特他说,一个AppDomain.Unload期间:





上午我误解了他们的话?
我做了以下解决方案,以重现意外的行为(在.NET 2.0 SP2):



所谓的接口包含此接口的类库项目:

 公共接口IXmlClass 
{
无效AllocateMemory(INT大小);

无效收集();
}

一个名为ClassLibrary1的类库项目,引用接口和包含此类:

 公共类XmlClass:MarshalByRefObject的,IXmlClass 
{

私人字节[] b:

公共无效AllocateMemory(INT大小)
{
this.b =新的字节[大小]
}

公共无效收集()
{
Console.WriteLine(+ AppDomain.CurrentDomain.FriendlyName +收集,在明确调用所以GC.Collect() () 方法);
GC.Collect的();
Console.WriteLine(集数:Gen0:{0}第一代:{1}第二代:{2},GC.CollectionCount(0),GC.CollectionCount(1),GC.CollectionCount(2) );
}

〜XmlClass()
{
Console.WriteLine(最后确定的AppDomain {0},AppDomain.CurrentDomain.FriendlyName);
}
}

一个控制台应用程序项目,引用接口项目做以下逻辑:

 静态无效的主要(字串[] args)
{
的AssemblyName的= AssemblyName.GetAssemblyName(ClassLibrary1.dll);
的AppDomain appDomain2 = AppDomain.CreateDomain(MYDOMAIN,空,AppDomain.CurrentDomain.SetupInformation);
IXmlClass C1 =(IXmlClass)appDomain2.CreateInstanceAndUnwrap(an.FullNameClassLibrary1.XmlClass);
Console.WriteLine(装载域{0},appDomain2.FriendlyName);
INT tenmb = 1024 * 10000;
c1.AllocateMemory(tenmb);
Console.WriteLine(集数:Gen0:{0}第一代:{1}第二代:{2},GC.CollectionCount(0),GC.CollectionCount(1),GC.CollectionCount(2) );
c1.Collect();
Console.WriteLine(空载域{0},appDomain2.FriendlyName);
AppDomain.Unload(appDomain2);
Console.WriteLine(卸载应用程序域后收藏数:Gen0:{0}第一代:{1}第二代:{2},GC.CollectionCount(0),GC.CollectionCount(1),GC.CollectionCount (2));
Console.WriteLine(请在默认域明确GC.Collect的());
GC.Collect的();
Console.WriteLine(集数:Gen0:{0}第一代:{1}第二代:{2},GC.CollectionCount(0),GC.CollectionCount(1),GC.CollectionCount(2) );
Console.ReadKey();
}



运行控制台应用程序时的输出是:

$ B $收藏乙

 加载域MYDOMAIN 
编号:Gen0:0第一代:0 2代:0
调用所以GC.Collect明确的()在MYDOMAIN收集()集合的方法
编号:Gen0:1第一代:1第二代:1
空载域MYDOMAIN
卸载应用程序域后,最终确定在集合的AppDomain MYDOMAIN
编号:Gen0:1第一代:第二代1:1
进行明确GC.Collect的()在默认域
收藏数:Gen0:2第一代:第二代2:2

事情需要注意:




  1. 垃圾收集每完成过程(只是回顾一下)


  2. 在AppDomain的对象是被卸载呼吁终结,但垃圾收集没有这样做。由AllocateMemory(创建)的10兆字节的对象将只在上面的例子中执行显式所以GC.Collect()(之后被收集,或者如果垃圾收集器将在一段时间后




其他说明:。这并不重要,如果XmlClass是终结或没有在上面的例子中,也会发生同样的行为。



问题:




  1. 为什么叫AppDomain.Unload不会导致垃圾回收有什么方法,使这一呼吁的结果在一个垃圾收集?


  2. 在AllocateMemory()我计划加载短暂的大型XML文档(小于或等于16 MB)将得到LargeObject堆,将是第2代的对象。有什么办法有(),而不诉诸明确GC.Collect的采集内存或其他种类的垃圾收集器的明确编程控制的?



解决方案

其他注意事项:



在一些邮件与杰弗里里希特交换谁还跟看看这个问题:



According to "Steven Pratschner" in "Customizing .NET Framework Common Language Runtime":

Am I misinterpreting their words?I did the following solution to reproduce the unexpected behavior (in .net 2.0 sp2):

An class library project called "Interfaces" containing this interface:

   public interface IXmlClass
    {
        void AllocateMemory(int size);

        void Collect();
    }

A class library project called "ClassLibrary1" which references "Interfaces" and contains this class:

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 console application project which references "Interfaces" project and does the following logic:

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();
}

The output when running the console application is:

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

Things to notice:

  1. Garbage collection is done per process (just a refresher)

  2. Objects in the appdomain that gets unloaded have the finalizer called but garbage collection is not done. The 10 megabyte object created by AllocateMemory() will only be collected after performing an explicit GC.Collect() in the above example (or if the garbage collector will at some time later.

Other notes: it doesn't really matter if XmlClass is finalizable or not. The same behavior occurs in the above example.

Questions:

  1. Why does calling AppDomain.Unload doesn't result in a garbage collection? Is there any way to make that call result in a garbage collection?

  2. Inside AllocateMemory() I plan to load short lived large xml documents (less or equal to 16 mb) that will get on LargeObject heap and will be generation 2 objects. Is there any way to have the memory collected without resorting to explicit GC.Collect() or other kind of explicit programmatic control of garbage collector?

解决方案

Additional Notes:

After some mail exchange with Jeffrey Richter who was kind enough to have a look at the question:

After taking his advice and looking into SOS (also removed the finalizer) it revealed this:

Before 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)

After AppDomain.Unload (same addresses, no heap compaction was done)

!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)

After GC.Collect(), addresses differ indicating heap compaction was done.

!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)

After more sos the conclusion I've reached is that it is surely by design, and that heap compaction is not necessarily done. The only thing you can really be sure during an AppDomain unload is that objects will get to be marked as unreachable and will be collected during the next garbage collection (which like I said, it's not done exactly when you unload your application domain, unless there's a coincidence).

EDIT: I've also asked Maoni Stephens, who works directly in the GC team. You can read her response somewhere in the comments here. She confirms that it is by design. Case closed :)

这篇关于为什么呼叫AppDomain.Unload不会导致垃圾收集?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-14 02:30