2016-05-26 42 views
0

我有一个使用WCF客户端与Java web服务进行通信的应用程序。该业务之一返回一个中等大的结果(约100 MB),有时我们得到一个OutOfMemoryException:OutOfMemoryException从WebServices接收数据

System.IO.MemoryStream.set_Capacity(Int32)已
System.IO.MemoryStream.EnsureCapacity(Int32)已
System.IO.MemoryStream.Write(字节[],的Int32,Int32)将
System.Xml.XmlMtomReader + MimePart.GetBuffer(的Int32,的Int32的ByRef)
System.Xml.XmlMtomReader.Initialize(System.IO.Stream, System.String, System.Xml.XmlDictionaryReaderQuotas,Int32)
System.Xml.XmlMtomReader.SetInput(System.IO.Stream, System.Text.Encoding [],System.String, System.Xml.XmlDictionaryReaderQuotas,的Int32, System.Xml.OnXmlDictionaryReaderClose)
System.ServiceModel.Channels.MtomMessageEncoder.TakeStreamedReader(System.IO.Stream, 系统。字符串)
System.ServiceModel.Channels.MtomMessageEncoder.ReadMessage(System.IO.Stream, 的Int32,System.String)
System.ServiceModel.Channels.HttpInput.ReadStreamedMessage(System.IO.Stream) System.ServiceModel。 Channels.HttpInput.ParseIncomingMessage(System.Exception ByRef)
System.ServiceModel.Channels.HttpChannelFactory + Ht tpRequestChannel + HttpChannelRequest.WaitForReply(System.TimeSpan) System.ServiceModel.Channels.RequestChannel.Request(System.ServiceModel.Channels.Message, System.TimeSpan)
System.ServiceModel.Dispatcher.RequestChannelBinder.Request(System.ServiceModel。 Channels.Message, System.TimeSpan)
System.ServiceModel.Channels.ServiceChannel.Call(System.String, 布尔,System.ServiceModel.Dispatcher.ProxyOperationRuntime, System.Object的[],System.Object的[],系统.TimeSpan)
System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(System.Runtime.Remoting.Messaging.IMethodCallMessage, System.ServiceModel.Dispatcher.ProxyOperat ionRuntime)
System.ServiceModel.Channels.ServiceChannelProxy.Invoke(System.Runtime.Remoting.Messaging.IMessage) System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(System.Runtime.Remoting.Proxies.MessageData 的ByRef,的Int32 )

我们WCF客户....

的数据量不够大的应用是创建一个真正的OutOfMemoryException异常32位应用程序耗时约400 - 600 MB和响应aroung 100Mb,所以其他事情必须发生。

有什么想法?

+2

如果你经常这样做(接收100mb大块),你的应用程序是32位的(为什么顺便说一下)?你的内存可能会被分割,因此没有_continuous_ 100MB的可用内存。 – Evk

+0

这是一个很好的文章:https://blogs.msdn.microsoft.com/ericlippert/2009/06/08/out-of-memory-does-not-refer-to-physical-memory/ – Evk

+0

碎片在这里是无关紧要的。所有当前的操作系统都使用分页虚拟内存。碎片化发生在连续的分配中。 – AhmadWabbi

回答

1

有两件事是导致这种行为:

  1. 32位进程只能寻址2GB(有时3GB),它是虚拟地址空间(1-2GB将被保留,并与32位指针,你只能寻址4GB)。

  2. 。NET在特殊的大型对象堆上存储大型对象(大于85000字节)。这个堆默认不是压缩的(并且在4.5.1之前的.NET版本中 - 根本没有压缩)。假设你分配了40MB,然后是10MB,然后是70MB的内存。过了一段时间,40MB和70MB被垃圾收集。现在你想分配100MB块。如果大对象堆被压缩 - 你可以有至少110MB的连续空闲地址空间。但它不是,所以你有两个40MB和70MB的差距,因此你不能分配你的100MB块。

所以即使有大量的这台机器上的空闲物理RAM(即使没有 - 总有交换),你可能无法得到一个指向的地址空间足够大的连续块。在这种情况下将会抛出OutOfMemoryException

夫妇的方式来解决这个问题:

  1. 使用64位进程。
  2. 通过重新使用字节数组来重用(池)内存块。例如WCF提供(并使用)这样的BufferManager
  3. .NET 4.5.1提供了一种压缩大对象堆的方法,但它并不能真正解决您的问题,我认为它只是延迟它。您可以压缩LOH一次(你不能强迫它来压缩每个集合)做:

    GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce; 
    GC.Collect();  
    
+0

如何将WCF客户端从缓冲转移到流?关于1)这里不是问题,因为该过程仅消耗600Mb。 –

+0

是的,转移到流应该也会有帮助。至于32位进程 - 这当然是一个问题,因为这里的问题是它不能分配足够大的_address space_块。在我描述的32位进程中,地址空间仅为2 GB。在64位的过程中,它有几兆字节。它与任何方式都没有关系,有多少物理内存,或者您的应用程序目前消耗多少内存(您提到的那些600MB)。 – Evk

0

尝试使用该WCF WebServiceHost和设置的配置不限制缓冲区或消息大小:

using System; 
    using System.ServiceModel.Web; 

    private WebServiceHost webHost; 

    public void Start() 
    { 
     webHost.Opening += ConfigureEnpointBinding; 

     webHost.Open(); 
    } 

    private void ConfigureEnpointBinding(object sender, EventArgs e) 
    { 
     var endpointBinding = (System.ServiceModel.WebHttpBinding) 
      ((WebServiceHost)sender) 
      .Description 
      .Endpoints 
      .Single(endpoint => endpoint.Contract.ContractType == typeof(IYourInterface)) 
      .Binding; 

     endpointBinding.MaxReceivedMessageSize = int.MaxValue; 
     endpointBinding.MaxBufferSize = int.MaxValue; 
    }