4

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

更新:我还发现为每个数组列表项使用 const 字符串与使用新对象有很大不同。代码很简单:

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

开始循环:内存 10,350,592

  • 抛出 OOM 异常
  • 数组大小:134,217,728

结束循环:内存550,408,192

开始循环:内存 550,731,776

  • 抛出 OOM 异常
  • 数组大小:134,217,728

结束循环:内存 551,682,048

开始循环:内存 551,813,120

  • 抛出 OOM 异常
  • 数组大小:134,217,728

结束循环:内存 551,772,160

开始循环:内存 551,903,232

  • 抛出 OOM 异常
  • 数组大小:67,108,864

结束循环:内存282,869,760

开始循环:内存 283,004,928

  • 抛出 OOM 异常
  • 数组大小:67,108,864

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

GC.Collect 手动触发

开始循环:内存14,245,888

  • 抛出 OOM 异常
  • 数组大小:67,108,864

结束循环:内存283,344,896

4

2 回答 2

8

这里有几点,综合起来,希望能给你足够的信息来回答你的问题:

  • 尽管它的名字,OutOfMemory 异常很可能意味着你的地址空间和物理 RAM 一样。
  • GC.Collect 不会收集所有未完成的 RAM。.Net 中的垃圾收集是 不确定的,这意味着无法强制运行时清理所有 RAM。
  • .Net 中的垃圾收集器是分的,这意味着当一个对象在收集中幸存下来时,它会向上移动到更高的代,使其更不可能被收集。
  • 当你的 OutOfMemory 异常被抛出时,你的数组可能已经在几次收集尝试中幸存下来,或者甚至被移动到了 LargeObjectHeap。
  • Array sizes are fixed. To add a new element to an array you must completely re-allocate the array. (You might get better test results using a structure like a list).
于 2009-08-26T18:50:18.403 回答
3

Since you are building arrays, I assume that you are building one big array for each run. If this is the case it will be stored in the Large Object Heap (as it will be > 85000 bytes). LOH is not compacted like the generational heap, so the drop in size you're seeing is probably due to heap fragmentation.

于 2009-08-26T19:01:52.997 回答