gpt4 book ai didi

c# - 是否收集了 MakeGenericType/泛型类型的垃圾?

转载 作者:可可西里 更新时间:2023-11-01 08:38:46 26 4
gpt4 key购买 nike

众所周知,在 .NET 中类型不会被垃圾回收,这意味着如果您正在使用 f.ex。 Reflection.Emit,你必须小心卸载 AppDomains 等等......至少我过去是这样理解事情是如何工作的。

这让我想知道泛型类型是否垃圾收集,更准确地说:使用 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 被垃圾收集的情况?

最佳答案

回答你的第一个问题:

不收集类型的通用构造。

但是,如果您构造 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

关于c# - 是否收集了 MakeGenericType/泛型类型的垃圾?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16087425/

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