2016-05-26 3 views
0

У меня есть приложение, которое взаимодействует с веб-службами Java с использованием клиентов WCF. Одна из услуг возвращает умеренно большой результат (около 100 Мб), а иногда мы получаем 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, системы. String)
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, Boolean, 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 МБ и ответ aroung 100Mb, так что что-то еще должно происходить.

Любая идея?

+2

Если вы это делаете (часто принимаете 100 Мб), а ваше приложение - 32-битное (почему, кстати?) - ваша память может быть фрагментирована, поэтому нет непрерывного 100 МБ фрагмента доступной памяти. – 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. процесс 32bit может адресовать 2 Гб (иногда 3GB) из его виртуального адресного пространства (1-2GB зарезервирован, и с 32- бит указатель вы можете адресовать только 4 ГБ).

  2. .NET хранит большие объекты (размером более 85000 байт) в специальной большой кучке объектов. Эта куча по умолчанию не compacted (и в версиях .NET до 4.5.1 - никогда не уплотняется вообще). Предположим, вы выделили 40 МБ, затем 10 МБ, а затем 70 МБ памяти. Через некоторое время было собрано 40 МБ и 70 МБ. Теперь вы хотите выделить кусок 100 МБ. Если крупномасштабная куча объектов была уплотнена, вы могли бы иметь не менее 110 МБ непрерывного свободного адресного пространства. Но это не так, и поэтому у вас есть два пробела в 40 МБ и 70 МБ, и из-за этого вы не можете выделить свой кусок на 100 МБ.

Таким образом, даже если есть много свободной физической памяти на этой машине (и даже если нет - всегда есть своп), вы не можете быть в состоянии получить указатель на непрерывный кусок адресного пространства достаточно большим. В этом случае будет выброшено OutOfMemoryException.

Пара путей решения этого:

  1. Использование 64-разрядного процесса.
  2. Повторное использование (пул) блоков памяти путем повторного использования байтовых массивов. Например, WCF предоставляет (и использует) такой BufferManager.
  3. .NET 4.5.1 обеспечивает способ уплотнения кучи больших объектов, но на самом деле он не решает вашу проблему, я думаю, просто откладывает ее. Вы можете уплотнить LOH один раз (вы не можете заставить его уплотнить на каждой коллекции), выполнив:

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

Как перемещать клиента WCF из буферизованного потока? Что касается 1), здесь проблема не в том, что процесс потребляет всего 600 МБ. –

+0

Да, переход на потоковое тоже поможет. Что касается 32-битного процесса - это, безусловно, проблема, потому что проблема здесь заключается в том, что он не может выделить достаточно большой фрагмент _address space_. В 32-битном адресном пространстве процесса всего 2 ГБ, как я описал. В 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; 
    } 
Смежные вопросы