难道MakeGenericType /泛型类型的垃圾回收?垃圾、类型、MakeGenericType

2023-09-03 01:56:01 作者:靡靡之音

众所周知,在.NET中的类型不是垃圾回收,这意味着,如果你玩弄f.ex. Reflection.Emit的,你必须要小心卸载应用程序域等等...至少这就是我用来理解事物是如何工作的。

It is well known in .NET that types are not garbage collected, which means that if you're playing around with f.ex. Reflection.Emit, you have to be careful to unload AppDomains and so on... At least that's how I used to understand how things work.

这使我想知道,如果一般类型的是的垃圾回收,更precise:以 MakeGenericType 创建泛型,让我们说。 ..例如基于用户输入。 : - )

That made me wonder if generic types are garbage collected, to be more precise: generics created with MakeGenericType, let's say... for example based on user input. :-)

所以,我构建了以下测试用例:

So I constructed the following test case:

public interface IRecursiveClass
{
    int Calculate();
}

public class RecursiveClass1<T> : IRecursiveClass 
                                  where T : IRecursiveClass,new()
{
    public int Calculate()
    {
        return new T().Calculate() + 1;
    }
}
public class RecursiveClass2<T> : IRecursiveClass
                                  where T : IRecursiveClass,new()
{
    public int Calculate()
    {
        return new T().Calculate() + 2;
    }
}

public class TailClass : IRecursiveClass
{
    public int Calculate()
    {
        return 0;
    }
}

class RecursiveGenericsTest
{
    public static int CalculateFromUserInput(string str)
    {
        Type tail = typeof(TailClass);
        foreach (char c in str)
        {
            if (c == 0)
            {
                tail = typeof(RecursiveClass1<>).MakeGenericType(tail);
            }
            else
            {
                tail = typeof(RecursiveClass2<>).MakeGenericType(tail);
            }
        }
        IRecursiveClass cl = (IRecursiveClass)Activator.CreateInstance(tail);
        return cl.Calculate();
    }

    static long MemoryUsage
    {
        get
        {
            GC.Collect(GC.MaxGeneration);
            GC.WaitForFullGCComplete();
            return GC.GetTotalMemory(true);
        }
    }

    static void Main(string[] args)
    {
        long start = MemoryUsage;

        int total = 0;
        for (int i = 0; i < 1000000; ++i)
        {
            StringBuilder sb = new StringBuilder();
            int j = i;
            for (int k = 0; k < 20; ++k) // fix the recursion depth
            {
                if ((j & 1) == 1)
                {
                    sb.Append('1');
                }
                else
                {
                    sb.Append('0');
                }
                j >>= 1;
            }

            total += CalculateFromUserInput(sb.ToString());

            if ((i % 10000) == 0)
            {
                Console.WriteLine("Current memory usage @ {0}: {1}", 
                                  i, MemoryUsage - start);
            }
        }

        Console.WriteLine("Done and the total is {0}", total);
        Console.WriteLine("Current memory usage: {0}", MemoryUsage - start);

        Console.ReadLine();
    }
}

正如你所看到的,泛型类型定义可能是递归,与标志着递归的最后一个'尾巴'类。并保证 GC.TotalMemoryUsage 是不是作弊,我也打开任务管理器。

As you can see, the generic types are defined 'possibly recursive', with a 'tail' class that marks the end of the recursion. And to ensure that GC.TotalMemoryUsage isn't cheating, I also opened Task Manager.

到目前为止好。接下来的事情我所做的就是这火兽了,虽然我在等待一个内存不足......我注意到,这是 - 出乎我的意料 - 不可以消耗更多的内存随着时间的推移。事实上,它显示了内存消耗略有下降时间。

So far so good. Next thing I did was fire this beast up and while I was waiting for an 'Out of memory' ... I noticed that it was - contrary to my expectations - not consuming more memory over time. In fact, it shows a slight drop in memory consumption in time.

有人可以解释一下吗?是通用类型的GC实际征收?如果是的话......有没有也Reflection.Emit的被垃圾回收的情况下?

Can someone please explain this? Are generic types actually collected by the GC? And if so... are there also Reflection.Emit cases that are garbage collected?

推荐答案

要回答你的第一个问题:

To answer your first question:

不收集各类通用结构。

不过,如果您构建 C&LT;字符串&GT; C&LT;对象&gt; ,CLR的实际生成的 code的方法的只有一次;因为参照串并参照对象保证是相同的大小,它可以安全地这样做。这是pretty的聪明。如果您构建 C&LT; INT&GT; C&LT;双&GT; 不过,获取生成的code的方法两次,一次为每个建设。 (假设在所有课程中产生的code的方法;方法是即时编译上的需求;这就是为什么它被称为jitting)

However, if you construct C<string> and C<object>, the CLR actually generates the code for the methods only once; since reference to string and reference to object are guaranteed to be the same size, it can do so safely. It's pretty clever. If you construct C<int> and C<double> though, the code for the methods gets generated twice, once for each construction. (Assuming that the code for the methods is generated at all of course; methods are jitted on demand; that's why its called jitting.)

要证明泛型类型没有收集,而是创建一个泛型类型

To demonstrate that generic types are not collected, instead create a generic type

class C<T> { public static readonly T Big = new T[10000]; }

C&LT;对象&gt; C&LT;字符串&GT; 共享任何code的方法生成的,但每个人都有自己的静态字段,这些字段将永远活着。越多的类型,你构建更多的内存空间将充满了那些大的阵列。

C<object> and C<string> share any code generated for the methods, but each one gets its own static fields, and those fields will live forever. The more types you construct, the more memory will be filled up with those big arrays.

和现在你知道为什么这些类型不能收取;我们没有办法知道是否有人会尝试在未来任何时间访问这些阵列中的一个成员。因为我们不知道什么时候最后一个数组访问将是,他们有长生不老的,因此包含的类型有长生不老了。

And now you know why those types cannot be collected; we have no way of knowing if someone is going to try to access a member of one of those arrays at any time in the future. Since we don't know when the last array access is going to be, they have to live forever, and therefore the type that contains it has to live forever too.

要回答你的第二个问题:有没有一种方法,使采集的动态发射的组件?

To answer your second question: Is there a way to make dynamically-emitted assemblies that are collected?

是的。该文件是在这里:

Yes. The documentation is here:

http://msdn.microsoft.com/en-us/library/dd554932.aspx

 
精彩推荐
图片推荐