Собирается ли мусор MakeGenericType / generic types?

в .NET хорошо известно, что типы не собираются в мусор, что означает, что если вы играете с f.бывший. Отображение.Emit, вы должны быть осторожны, чтобы выгрузить AppDomains и так далее... По крайней мере, так я раньше понимал, как все устроено.

это заставило меня задуматься, если общие типы are собранный мусор, если быть более точным: дженерики, созданные с 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 не обманывает, я также открыл Диспетчер задач.

пока все хорошо. Следующее, что я сделал, это запустил этого зверя, и пока я ждал и "из памяти"... Я заметил, что это было - вопреки моим ожиданиям - не потребляя больше памяти с течением времени. На самом деле, он показывает a небольшое падение потребления памяти во времени.

может кто-нибудь объяснить это? Действительно ли универсальные типы собираются GC? И если так... есть также отражение.Выбрасывать дела, которые собирают мусор?

1 ответов


чтобы ответить на ваш первый вопрос:

общие конструкции типов не собираются.

однако, если вы построите C<string> и C<object>, среда CLR фактически генерирует код для методов только один раз; поскольку ссылка на строку и ссылка на объект гарантированно имеют одинаковый размер, это можно сделать безопасно. Это довольно умно. Если вы построите C<int> и C<double> хотя, код для методов создается дважды, один раз для каждого строительство. (Предполагая, что код для методов генерируется вообще, конечно; методы jitted по требованию; вот почему его называют 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