情况是我正在对远程服务器进行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是我无能为力的。
我问过自己的一些问题:
问题是:对于要尝试的事情有任何建议或建议吗?
答案 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。