C# 循环范围和内存问题

C# 循环范围和内存问题,c#,C#,假设我有以下代码 StringBuilder sb = new StringBuilder(); for (int i = 0; i < 10000; i++) { MyCustomClass myObj = new MyCustomClass(); sb.Append(myObj.RenderShortString()); } Console.Write(sb.ToString()); StringBuilder sb=新建StringBuilder(); 对于(int i=0

假设我有以下代码

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++)
{
  MyCustomClass myObj = new MyCustomClass();
  sb.Append(myObj.RenderShortString());
}
Console.Write(sb.ToString());
StringBuilder sb=新建StringBuilder();
对于(int i=0;i<10000;i++)
{
MyCustomClass myObj=新的MyCustomClass();
sb.Append(myObj.RenderShortString());
}
Console.Write(sb.ToString());
假设MyCustomClass是一个非常大的对象。例如,假设它创建并保存一个包含1MB字符串的内部成员。RenderShortString()方法只渲染长度约为100个字符的字符串

注意这个循环10000次

我有一些基本上像这样的东西,它在循环中导致System.OutOfMemory异常


我的问题与垃圾收集器何时清理为myObj的每个实例分配的内存空间有关。我不认为我和StringBuilder有什么问题,但我可能错了。我感觉myObj的实例被分配到内存中,但直到循环退出后才可用于清理。这是正确的吗?如果是这样,我如何告诉应用程序,一旦我得到渲染字符串,我就完成了该实例的处理?

简单回答:你永远不知道。NET垃圾收集不是确定性的。您可以使用
System.GC.Collect
方法强制垃圾收集。除此之外,GC只保证分配给不可访问对象的内存最终会被释放。

简单回答:你永远不知道。NET垃圾收集不是确定性的。您可以使用
System.GC.Collect
方法强制垃圾收集。除此之外,GC只保证分配给不可访问对象的内存最终会被释放。

您在.net中看到了垃圾收集的“功能”。一旦超出范围,对象就会被销毁,每个myObj在每次迭代中都超出范围,但您不知道什么时候,因为GC是不确定的

这里有一些解释:

另外,这里有一个关于.net的GC的有趣研究。它建议尽可能避免在循环中使用“new”

您在.net中看到了垃圾收集的一个“功能”。一旦超出范围,对象就会被销毁,每个myObj在每次迭代中都超出范围,但您不知道什么时候,因为GC是不确定的

这里有一些解释:

另外,这里有一个关于.net的GC的有趣研究。它建议尽可能避免在循环中使用“new”


您可能真的想再看一看StringBuilder是如何工作的,因为这可能会消耗大量内存

也许不是根本问题,如果您说MyCustomClass内存很重,但它可能会导致进程被推到边缘

每次StringBuilder耗尽空间时,它都会重新分配一个新缓冲区,大小为原始缓冲区的两倍,复制旧字符,并让旧缓冲区获得GC'd。您可能只是使用了足够的内存(称为x),因此2x比允许分配的内存大。您可能需要确定字符串的最大长度,并将其传递给StringBuilder的构造函数,以便进行预分配,而不受加倍重新分配的影响


您可能真的想再看一看StringBuilder是如何工作的,因为这可能会消耗大量内存

也许不是根本问题,如果您说MyCustomClass内存很重,但它可能会导致进程被推到边缘

每次StringBuilder耗尽空间时,它都会重新分配一个新缓冲区,大小为原始缓冲区的两倍,复制旧字符,并让旧缓冲区获得GC'd。您可能只是使用了足够的内存(称为x),因此2x比允许分配的内存大。您可能需要确定字符串的最大长度,并将其传递给StringBuilder的构造函数,以便进行预分配,而不受加倍重新分配的影响


RenderShortString()
完成时,或者在某些情况下,甚至在该方法执行期间,每个
MyCustomClass
实例都有资格收集

实际的垃圾收集只会在垃圾收集器感觉像它的时候发生,但是很可能会很快从gen0收集
MyCustomClass
实例

请注意,
MyCustomClass
对象本身并不大,只是因为它们引用了大字符串。这些大字符串将被分配到上,上的字符串仍然是垃圾收集的,但没有被压缩。如果你发现你的应用程序占用了相当多的内存,那么很可能是
MyCustomClass
的实例已经被垃圾收集,但是字符串没有

实际上,在.NET中创建一个非常大的自定义对象非常困难。显而易见的例子是:

  • 长线
  • 包含大量元素的数组
  • 盒装结构,其中结构本身包含大型固定缓冲区(这应该很少)

在大多数情况下,对象本身非常小,但有很多对象。

每个
MyCustomClass
实例都可以在
RenderShortString()
完成时收集,或者在某些情况下,甚至在该方法执行期间收集

实际的垃圾收集只会在垃圾收集器感觉像它的时候发生,但是很可能会很快从gen0收集
MyCustomClass
实例

请注意,
MyCustomClass
对象本身并不大,只是因为它们引用了大字符串。这些大字符串将被分配到上,上的字符串仍然是垃圾收集的,但没有被压缩。如果你发现你的应用程序占用了相当多的内存,那么很可能是
MyCustomClass
的实例被垃圾收集了,但是字符串却没有