2

我有一个非常大的文本文件要解析(~2GB)。由于各种原因,我必须逐行处理文件。我通过将文本文件加载到内存(我正在运行解析器的服务器有足够的内存)来做到这一点var records = Regex.Split(File.ReadAllText(dumpPath, Encoding.Default), @"my regex here").Where(s => !string.IsNullOrEmpty(s));。这消耗的 RAM 相当于文本文件的大小加上几 MB 的IEnumerable开销。到目前为止,一切都很好。然后我与foreach (var recordsd in records) {...}

有趣的部分来了。我在 foreach 循环中做了很多字符串操作和正则表达式。然后程序很快就会用 System.OutOfMemoryException 轰炸,即使我在 foreach 循环中从未使用超过几 kB。我使用我选择的分析器(ANTS 内存分析器)制作了一些内存快照,在堆上看到了数百万个第 2 代字符串对象,消耗了所有可用内存。

看到这一点,我 -就像一个测试一样- 在每次 foreach 迭代结束时都包含 a GC.Collect();,瞧,问题解决了,不再出现内存不足的异常(因为永久垃圾收集,程序现在运行得非常缓慢)。唯一消耗的内存是实际文件的大小。

现在我无法解释为什么会发生这种情况以及如何防止它。据我了解,当变量超出范围并且没有更多(活动)引用时,应该将其标记为垃圾收集,对吗?

另一方面,我试图在一台非常庞大的机器(64GB RAM)上运行该程序。程序成功完成,但在关闭之前从未释放一个字节的内存。为什么?如果没有更多对对象的引用加上如果对象超出范围,为什么永远不会释放内存?

4

1 回答 1

5

现在我无法解释为什么会发生这种情况以及如何防止它。据我了解,当变量超出范围并且没有更多(活动)引用时,应该将其标记为垃圾收集,对吗?

不。没有被“标记”为垃圾收集的东西,变量不是垃圾收集:对象是。并且已经在 gen2 中的对象直到 GC 下一次查看 gen2 时才会被垃圾收集,这种情况比较少见。

由于各种原因,我必须逐行处理文件。

然后就是您的答案:File.ReadLines如果您使用的是 .NET 4,请使用,如果您不是,请编写等效的(这很容易)。然后你不需要一次在内存中的整个文件 - 只需要一行。您的内存使用量绝对应该直线下降。(请注意ReadLines不是 ReadAllLines- 后者会将整个文件读入字符串数组,这不是您想要的。)

另一方面,我试图在一台非常庞大的机器(64GB RAM)上运行该程序。程序成功完成,但在关闭之前从未释放一个字节的内存。为什么?

如果您谈论的是进程从操作系统中获取的内存,我不相信CLR 会释放内存。我假设它采用的方法是,如果您曾经使用过这么多内存,您可能会再次使用这么多。

于 2012-04-18T09:17:01.217 回答