循环范围和内存问题

时间:2009-08-22 18:45:01

标签: c#

假设我有以下代码......

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++)
{
  MyCustomClass myObj = new MyCustomClass();
  sb.Append(myObj.RenderShortString());
}
Console.Write(sb.ToString());

并假设MyCustomClass是一个非常大的对象。例如,假设它创建并保存一个包含1MB字符串的内部成员。 RenderShortString()方法只是呈现一个长度约为100个字符的字符串。

注意这循环10000次。

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

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

5 个答案:

答案 0 :(得分:5)

您正在.net中看到垃圾收集的“功能”。一旦超出范围,对象将被销毁,并且每次myObj在每次迭代时都超出范围但是你不知道GC何时是非确定性的。

以下是一些解释:Loops and Garbage Collection

此外,这是一项关于.net的GC的有趣研究。它建议尽可能避免在循环内使用“new”。

http://nerds-central.blogspot.com/2008/10/net-garbage-collector-pain.html

答案 1 :(得分:4)

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

答案 2 :(得分:2)

你可能真的想再看一下StringBuilder的表现,因为这也可能非常耗费内存。

也许不是根本问题,如果你说MyCustomClass的内存很重,但它可能有助于推动整个过程。

  

每次StringBuilder空间不足时,它会重新分配两倍于原始缓冲区大小的新缓冲区,复制旧字符,并让旧缓冲区得到GC。您可能只是使用足够的(称之为x),使得2x大于您允许分配的内存。您可能想要确定字符串的最大长度,并将其传递给StringBuilder的构造函数,以便您预先分配,并且您不会受到双倍重新分配的支配。

See this topic

答案 3 :(得分:2)

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

实际的垃圾收集只会在垃圾收集器的情况下发生,但很可能很快会从gen0收集MyCustomClass个实例。

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

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

  • 长串
  • 包含大量元素的数组
  • 盒式结构,其中结构本身包含大的固定缓冲区(这应该是罕见的)

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

答案 4 :(得分:1)

您应该运行一个分析器来查看哪些对象分配了多少内存。 Visual Studio的分析器将为您提供该数字,甚至每代中有多少个对象。