1

我很少来这里寻求帮助,但这让我发疯:我正在读取一个包含任意数量项目的 xml 文件,每个项目都有一个 b64 编码文件(以及一些随附的元数据)。XmlDocument最初我只是把整个文件读入一个XmlDocument. 所以我重写了代码来代替使用XmlTextReader,如果问题是程序被发送了一个带有大量大小合理的附件的 xml 文件,这很好用......但仍然存在一个大问题,这就是我转向你的地方:

如果我的 xml 阅读器位于 File 元素上,该元素包含一个巨大的值(例如,500MB),我调用reader.ReadElementContentAsString(),我现在有一个占用 500MB 的字符串(或者可能是 OutOfMemoryException)。在这两种情况下,我想做的只是写入日志,“那个文件附件太大了,我们将忽略它并继续前进”,然后转到下一个文件。但似乎我刚刚尝试读取的字符串并没有被垃圾收集,所以实际发生的是字符串占用了所有 RAM,并且之后它尝试读取的每个其他文件也会引发 OutOfMemoryException,即使大多数的文件将非常小。

回想一下:此时,我正在将元素的值读入本地字符串,所以我预计它会立即有资格进行垃圾收集(因此最迟在程序尝试阅读下一项并发现它没有可用的内存)。但是我已经尝试了一切,以防万一:将字符串设置为null,调用显式GC.Collect()......没有骰子,任务管理器指示GC只收集了大约40k,它刚刚请求存储字符串的~500MB,我仍然会出现内存不足异常尝试读取其他内容。

似乎没有任何方法可以在XmlTextReader不读取该元素的情况下知道 xml 元素中包含的值的长度,所以我想我一直在阅读字符串......我错过了什么,或者真的没有如何从 xml 文件中读取一个巨大的值而不完全破坏你的程序之后做任何事情的能力?我要疯了。

我读过一些关于 C# 的 GC 和 LOH 的内容,但我读到的任何内容都不会向我表明这会发生......

如果您需要任何进一步的信息,请告诉我,谢谢!

编辑:我确实意识到该进程是作为 32 位进程运行的,这意味着它比应有的更多地缺乏内存。解决了这个问题,这不再是一个问题,但它仍然是我想修复的行为。(需要更多和/或更大的文件才能达到抛出 OutOfMemoryException 的点,但是一旦抛出,我似乎仍然无法及时回收该内存。)

4

3 回答 3

1

我有一个与用于将大文件传输为 base64 字符串的肥皂服务类似的问题。

那时我使用 XDocument 而不是 XmlDocument,这对我有用。

于 2013-04-18T16:39:37.473 回答
1

您可以使用XmlReader.ReadValueChunk方法一次读取一个“块”元素的内容,而不是尝试一次读取整个内容。这样,您可能会在某些时候决定数据太大,然后忽略它并记录事件。StringBuilder可能是将收集的 char 数组块组合在一个字符串中的最佳方法。

如果你想用 释放内存GC.Collect(),你可以用 强制立即终结和内存释放GC.WaitForPendingFinalizers()。这可能会影响性能(甚至挂起,请参阅链接后面的描述),但是您应该摆脱大对象,假设您不再有对它们的任何实时引用(即局部变量已经超出范围或其值设置为 null) 并正常继续操作。当内存消耗是一个问题并且您真的想强制摆脱多余的内存分配时,您当然应该将此作为最后的手段。

我已经成功地GC.Collect();GC.WaitForPendingFinalizers();在内存敏感的环境中使用组合来将应用程序的内存占用保持在 100MB 以下,即使它读取一些非常大的 XML 文件 (>100MB) 也是如此。为了提高性能,我还使用Process.PrivateMemorySize64跟踪内存消耗和仅在达到特定限制后强制完成。在我改进之前,内存消耗确实有时会超过 1GB!

于 2014-01-09T09:25:35.497 回答
0

我不肯定这是这种情况,但我认为你需要处理XmlTextReader. 将过大节点之后的节点的xmlpath保存为字符串,将你的大量字符串设置为null,然后XmlTextReader在大节点之后的节点处处理并重新打开它。据我了解,如果您将字符串设置为null,或者超出范围,GC 应该尽快释放该内存。在我看来,您正在释放字符串,但您继续使用XmlTextReader现在持有大量内存的字符串进行操作。

想到的另一个想法是尝试在一个unsafe块内执行此操作,然后显式释放内存,但是,这看起来不太可能(其他人可能知道,但在环顾四周后,似乎不安全的块仍然是 GC 'd,它只是给你指点)。还有一种选择,虽然 imo 很糟糕,但它是制作一个用于在 C 或 C++ 中解析的 dll 并从您的 C# 项目中调用它。

在做最后一个疯狂的事情之前尝试第一个建议:)

于 2013-04-18T16:35:18.990 回答