使用foreach循环解析大型文本文件的奇怪行为(C#.NET 4)

使用foreach循环解析大型文本文件的奇怪行为(C#.NET 4),c#,memory-management,garbage-collection,out-of-memory,C#,Memory Management,Garbage Collection,Out Of Memory,我有一个非常大的文本文件要解析(~2GB)。由于各种原因,我必须逐行处理文件。为此,我将文本文件加载到内存中(我运行解析器的服务器有足够的内存),并使用var records=Regex.Split(file.ReadAllText(dumpPath,Encoding.Default),@“my Regex here”)。其中(s=>!string.IsNullOrEmpty(s))。这将消耗相当于文本文件大小的RAM加上几MB的IEnumerable开销。到现在为止,一直都还不错。 然后我使用

我有一个非常大的文本文件要解析(~2GB)。由于各种原因,我必须逐行处理文件。为此,我将文本文件加载到内存中(我运行解析器的服务器有足够的内存),并使用
var records=Regex.Split(file.ReadAllText(dumpPath,Encoding.Default),@“my Regex here”)。其中(s=>!string.IsNullOrEmpty(s))。这将消耗相当于文本文件大小的RAM加上几MB的
IEnumerable
开销。到现在为止,一直都还不错。 然后我使用
foreach(记录中的var recordsd){…}

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

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

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

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

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

不,没有“标记”垃圾收集这样的事情,变量也不是垃圾收集的:对象是。并且,在GC下次查看gen2之前,已经在gen2中的对象不会被垃圾收集,这是相对罕见的

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

然后是你的答案:如果你在使用.NET4,就使用它,如果你不使用,就编写相应的代码(这很容易)。这样,您就不需要一次将整个文件存储在内存中,只需要一行。你的内存使用率应该绝对下降。(请注意,这是
ReadLines
,而不是
ReadAllLines
——后者会将整个文件读入字符串数组,这不是您想要的。)

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


如果你说的是进程从操作系统获取的内存,我不相信CLR会释放内存。我认为,如果你曾经使用过那么多内存,那么你可能会再次使用那么多内存。

好的,我可能打字太快,没有重新阅读我实际写的内容。因此,出于各种其他原因,我需要将整个文本文件存储在内存中,因为我也在对文本文件进行大量随机访问。还有,我只是想解释一下我看到的。谢谢。我只是想详细说明一下。该文件是由一个DNA取样设备生成的,其文件格式相当隐秘。一些DNA样本传播不止一条线,其他的则没有。一行中的值的解释取决于其他行中的值。等等目标是将这个巨大的文本文件转换为一组自定义对象,这些对象将被分析并保存到数据库中。解释所有这些东西有点复杂,但在解析文件时将其视为一个整体是很重要的。我只需要知道为什么要调用GC.Collect();似乎解决了这个问题,我真的很想提高你的评论,但我的代表太低了:(@lightxx:While打电话给
GC.Collect()不是个好主意)
你自己,可能会有一些奇怪的情况下它是有用的-听起来这可能是其中之一。这里的真正问题不是为什么字符串会在第2代结束吗?你必须以某种方式保持它们足够长的时间,以错过gen0和gen1清理。一个旁注。你可以尝试执行
GC.Collect(2,GCCollectionMode.Optimized)
加快采集速度。