了解.NET GC和OutOfMemory异常

时间:2009-08-26 18:34:20

标签: .net garbage-collection out-of-memory

我正在对.NET 2.0 Windows服务应用程序中的OutOfMemory异常进行故障排除。为了更好地理解这个问题,我首先编写了一个简单的.NET WinForm测试应用程序,该应用程序通过构建ArrayList来生成OOM异常,直到抛出OOM异常为止。捕获并记录异常,我可以单击表单按钮再次运行OOME。我发现的奇怪的事情是在第4次运行时,在下一个OOME之前消耗的内存量大约是一半。每次运行时,下面列出的结果都是一致的。 Eyeballing TaskManager也确认了这一行为。不幸的是,当试图获得更好的统计数据时,Perfmon冻结了。有人可以解释为什么3次运行后内存限制会降低吗?我对GC的理解相当浅薄。你还可以看到我在经过几次运行后运行了一个GC.Collect(),但它没有帮助降低限制。

更新:我还发现使用const字符串与每个arraylist项目的新对象有很大的不同。代码很简单:

const string TEST_TEXT = "xxxxxxxxxx";
ArrayList list = new ArrayList();
while (true)
{
    list.Add(TEST_TEXT);
}

开始循环:记忆10,350,592

  • OOM Exception Thrown
  • 数组大小:134,217,728

结束循环:记忆 550,408,192

启动循环:内存550,731,776

  • OOM Exception Thrown
  • 数组大小:134,217,728

结束循环:记忆551,682,048

开始循环:记忆551,813,120

  • OOM Exception Thrown
  • 数组大小:134,217,728

结束循环:记忆551,772,160

启动循环:内存551,903,232

  • OOM Exception Thrown
  • 数组大小:67,108,864

结束循环:记忆 282,869,760

启动循环:内存283,004,928

  • OOM Exception Thrown
  • 数组大小:67,108,864

结束循环:内存282,910,720

GC.Collect手动触发

开始循环:记忆 14,245,888

  • OOM Exception Thrown
  • 数组大小:67,108,864

结束循环:记忆 283,344,896

2 个答案:

答案 0 :(得分:8)

以下几点,希望能够为您提供足够的信息来回答您的问题:

  • 尽管它的名字是OutOfMemory exceptions are just as likely to mean you are out of Address Space as physical RAM.
  • GC.Collect 收集所有未完成的RAM。 .Net中的垃圾收集非确定性,这意味着强制运行时无法清理所有RAM。
  • .Net中的垃圾收集器是世代,这意味着当一个对象在收集中存活时,它会向上移动到更高的一代,从而更不可能收集它。
  • 当你的OutOfMemory异常被抛出时,你的数组可能已经经历了几次收集尝试,或者甚至被移动到了LargeObjectHeap。
  • 数组大小是固定的。要向阵列添加新元素,必须完全重新分配阵列。 (您可以使用类似列表的结构获得更好的测试结果。)

答案 1 :(得分:3)

由于您正在构建数组,我假设您正在为每次运行构建一个大数组。如果是这种情况,它将被存储在大对象堆中(因为它将> 85000字节)。 LOH不像分代堆那样被压缩,所以你看到的大小下降可能是由于堆碎片造成的。