被MakeGenericType /泛型类型的垃圾回收?(Are MakeGenericType /

2019-08-31 12:12发布

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

这使我想知道,如果通用类型垃圾回收,更精确地说:与创建泛型MakeGenericType ,比方说......例如基于用户输入。 :-)

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

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没有作弊,我也打开任务管理器。

到现在为止还挺好。 我做下一件事是火灾此兽了,虽然我在等待一个“内存不足” ......我注意到,这是-出乎我的意料- 消耗随着时间的推移更多的内存。 事实上,它显示的时间内存消耗略有下降。

可有人请解释一下吗? 是通用的类型实际上是由GC收集的? 如果是的话......在那里也Reflection.Emit的情况是垃圾回收?

Answer 1:

要回答你的第一个问题:

类型的通用结构不被回收。

但是,如果构造C<string>C<object> ,该CLR实际生成用于方法仅当代码 ; 因为对字符串的参考和借鉴的对象都保证相同的大小,它可以安全地这样做。 这是非常聪明的。 如果构建C<int>C<double>虽然,对于这些方法的代码获取生成两次,一次用于每个建筑。 (假设为方法的代码在所有课程的生成;方法是实时编译的需求,这就是为什么它被称为jitting。)

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

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

C<object>C<string>分担的方法产生的任何代码,但每一个都有自己的静态字段,并且这些字段将永远活。 您构建更多类型,更多的内存将与那些大阵被填满。

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


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

是。 该文件是在这里:

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



Answer 2:

无关的代码共享或代码不共享,各MakeGenericType尝试将为metada这将消耗内存中创建新的内部CLR类。 类型对象在CLR代码中直接创建(非托管代码),存在每个类型的对象只有一个实例,所以你可以对它们进行比较参考平等。 CLR本身持有对它的引用,使他们不能被GC'ed但在我的测试中,我证实了GC可以移动它们。

编辑:由CLR参考保持可能是弱引用,以便挖RuntimeTypeHandle.cs源后,我看到

internal bool IsCollectible()
{
    return RuntimeTypeHandle.IsCollectible(GetTypeHandleInternal());
}

这很可能是假的,考虑埃里克利珀



文章来源: Are MakeGenericType / generic types garbage collected?