如何最大化大对象堆中最大的连续内存块

时间:2010-04-22 11:44:50

标签: c# wcf out-of-memory stringbuilder

情况是我正在对远程服务器进行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被提前抛出(因为我猜在需要的时候,它可能实际上并不需要那么多,所以我的检查引起的问题比解决的问题多得多。有些opinions是我无能为力的。

我问过自己的一些问题:

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

问题是:对于要尝试的事情有任何建议或建议吗?

4 个答案:

答案 0 :(得分:5)

您可以查看绑定的 TransferMode 属性,看看您是否符合将其从默认值“缓冲”更改为“流式传输”的要求或“的 StreamedResponse ”。

另外,请查看 maxBufferPoolSize maxBufferSize 的值。增加所用内部缓冲区的大小有助于提高内存利用率,尤其是在处理大量消息时。

如果您收到大量邮件,

maxReceivedMessageSize 也可能已设置,但我也会审核该值。

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

我对你在帖子中使用的绑定没有感觉,但我相信这些设置在主要设置中很常见。例如,查看basicHttpBinding上的MSDN文档。

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

答案 1 :(得分:2)

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

答案 2 :(得分:1)

我认为您的问题可能是使用XmlSerializer导致的程序集泄漏,而不是使用此MSDN article中指示的两个构造函数之一:

  

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

     

XmlSerializer.XmlSerializer(类型):

     

XmlSerializer.XmlSerializer(类型,   字符串)

     

如果你使用其他任何一个   构造函数,多个版本的   生成相同的程序集,永远不会   卸载,导致内存   泄漏和表现不佳。

很好,呵呵。答案是缓存你的XmlSerializer(假设你甚至创建它)。

要真正弄明白你需要做Tess告诉你的事情。她是一个天才。

答案 3 :(得分:1)

如果可能的话,我会选择基于流的方法并结合使用仅向前Xml解析器,这样可以提供更好的性能。

如果您不必使用WCF,则可以编写自己的HttpRequest,然后将响应传递给XmlDeserializer,然后解析响应。它可以让您更好地控制和洞察问题实际发生的位置。您还可以尝试使用模拟服务来返回您要查找的类型的非常大的文档。 我们对LOH碎片也有很多麻烦,所以我真的感觉到你的痛苦。

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