5

情况是我正在对远程服务器进行 WCF 调用,该服务器将 XML 文档作为字符串返回。

大多数时候这个返回值是几K,有时是几十K,偶尔几百K,但很少有可能是几兆(第一个问题是我无法知道)。

正是这些罕见的情况造成了悲伤。我得到一个开始的堆栈跟踪:

System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown.
   at System.Xml.BufferBuilder.AddBuffer()
   at System.Xml.BufferBuilder.AppendHelper(Char* pSource, Int32 count)
   at System.Xml.BufferBuilder.Append(Char[] value, Int32 start, Int32 count)
   at System.Xml.XmlTextReaderImpl.ParseText()
   at System.Xml.XmlTextReaderImpl.ParseElementContent()
   at System.Xml.XmlTextReaderImpl.Read()
   at System.Xml.XmlTextReader.Read()
   at System.Xml.XmlReader.ReadElementString()
   at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderMDRQuery.Read2_getMarketDataResponse()
   at Microsoft.Xml.Serialization.GeneratedAssembly.ArrayOfObjectSerializer2.Deserialize(XmlSerializationReader reader)
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events)
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle)
   at System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall)
   at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters)

我已经阅读过,这是因为大对象堆太碎片化了,所以即使在调用之前快速检查 StringBuilder.EnsureCapacity 只会导致提前抛出 OutOfMemoryException (因为我在猜测需要什么,它实际上可能不需要那么多,所以我的检查导致的问题多于解决的问题)。有些意见是我对此无能为力。

我问过自己的一些问题:

  • 使用更少的内存 - 你检查过泄漏吗?是的。内存使用量会上升和下降,但没有基本的增长可以保证这种情况发生。有时它失败了,它在之前的那个阶段成功了。
  • 转移较小的金额不是一个选项,这是我无法控制的第三方网络服务(或者至少需要很长时间才能解决,同时我仍然有问题)
  • 你能对 LOH 做些什么来降低它失败的可能性吗?...现在这是最富有成效的课程。这是一个 32 位进程(它必须出于各种政治、技术和无聊的原因),但通常有数百兆免费(我们见过失败的最大数量的倍数)。
  • 我们可以监控 LOH 吗?使用 perfmon 我可以跟踪堆的大小,但我认为没有办法监控最大的可用连续内存块。

问题是:对尝试的事情有什么建议或建议吗?

4

4 回答 4

5

您可以查看绑定的TransferMode属性,看看您是否满足将其从默认值“Buffered”更改为“ Streamed ”或“ StreamedResponse ”的要求。

此外,查看maxBufferPoolSizemaxBufferSize的值。增加使用的内部缓冲区的大小可以帮助提高内存利用率,尤其是在处理大型消息时。

如果您收到大消息, maxReceivedMessageSize也可能已经设置,但我也会查看该值。

我已经看到上面的值之一,如果您超过阈值,则会失败并显示与内存相关的模糊消息。最初的异常实际上被显示在我的应用程序中的消息所隐藏。启用WCF 跟踪有助于诊断问题并查看真正的错误 - 我需要增加上述绑定属性中的一个或多个值。

我没有从您的帖子中感受到您使用的绑定,但我相信这些设置在主要设置中很常见。例如,查看有关basicHttpBinding的 MSDN 文档。

如果它真的是 LOH 碎片,那么一旦调整工作已经用尽,就没有什么可做的了。可能需要滚动回收应用程序来减轻它(我讨厌推荐),但如果你已经用尽了其他努力,你可能会留下来。

于 2010-04-22T12:32:06.490 回答
2

我无法解决任何 WCF 特定问题,但如果您需要为 32 位进程最大化 LOH 空间,您应该让应用程序知道大地址并在 64 位上运行它。在 64 位 Windows 上运行时,大地址感知 32 位进程将能够寻址整个 4 GB 地址空间。这将在进程通常使用的地址空间之上为您提供相当大的内存块。

于 2010-12-05T10:34:18.357 回答
1

如果可能的话,我会采用基于流的方法并结合使用仅向前的 Xml 解析器,这也应该会给你更好的性能。

如果您不是绝对必须使用 WCF,您可以编写自己的 HttpRequest,然后将响应传递给 XmlDeserializer,然后像这样解析响应。它可能使您对问题实际发生的位置有更多的控制和洞察力。您还可以尝试使用一个模拟服务来返回您正在查找的类型的非常大的文档。LOH 碎片化我们也很头疼,所以我真的能感受到你的痛苦。

我在构建缓冲区时注意到的一个问题 .NET 倾向于在每次缓冲区被填满时使容量翻倍,这会导致内存碎片,因为对于大小为 10mb 的文档,需要在多个步骤中分配内存。如果您事先知道所需的缓冲区大小,那么一次分配它会更有效。因此,如果您知道传入的文档有多大,您可以创建一个具有该大小的 StringBuilder。

于 2010-12-05T10:28:03.633 回答
1

我认为您的问题可能是由于使用 XmlSerializer 而未使用此MSDN 文章中所示的两个构造函数之一引起的程序集泄漏:

为了提高性能,XML 序列化基础结构动态生成程序集以序列化和反序列化指定类型。基础结构查找并重用这些程序集。此行为仅在使用以下构造函数时发生:

XmlSerializer.XmlSerializer(类型)

XmlSerializer.XmlSerializer(类型,字符串)

如果您使用任何其他构造函数,则会生成同一程序集的多个版本并且永远不会卸载,这会导致内存泄漏和性能下降。

不错,嗯。答案是缓存您的 XmlSerializer(假设您甚至创建了它)。

要真正弄清楚,你需要按照苔丝告诉你的去做。她是个绝世天才。

于 2010-06-03T13:49:50.390 回答