Как максимизировать самый большой непрерывный блок памяти в куче больших объектов - PullRequest
5 голосов
/ 22 апреля 2010

Ситуация такова, что я делаю вызов 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 ]

5 голосов
/ 22 апреля 2010

Вы можете просмотреть свойство TransferMode вашей привязки, чтобы узнать, соответствуете ли вы требованиям, чтобы изменить его значение по умолчанию "Буферизованное", на " Потоковое " или " StreamedResponse ».

Также просмотрите значения maxBufferPoolSize и maxBufferSize . Увеличение размера используемых внутренних буферов может помочь с использованием памяти, особенно при обработке больших сообщений.

maxReceivedMessageSize также, вероятно, уже установлен, если вы получаете большие сообщения, но я бы также рассмотрел это значение.

Я видел одно из значений выше, если при превышении порога произойдет сбой с неясным сообщением, связанным с памятью. Исходное исключение было фактически скрыто сообщением, которое появилось в моем приложении. Включение WCF Tracing помогло диагностировать проблему и увидеть реальную ошибку - мне нужно было увеличить значение одного или нескольких свойств привязки выше.

Мне не пришло в голову ощущение привязки вашего использования из вашего поста, но я считаю, что эти настройки распространены среди основных. Ознакомьтесь с документацией MSDN по basicHttpBinding , например.

Если это действительно фрагментация LOH, с этим ничего не поделаешь, когда усилия по настройке исчерпаны. Для его смягчения может потребоваться повторное использование приложения (я ненавижу рекомендовать это), но если вы исчерпали другие усилия, вам может потребоваться это.

2 голосов
/ 05 декабря 2010

Я не могу решить ни одну из специфических проблем WCF, но если вам нужно максимизировать пространство LOH для 32-битного процесса, вы должны сделать приложение большим адресом осведомленным и запустить его на 64-битной. 32-разрядный процесс, поддерживающий большие адреса, сможет работать с адресным пространством всего 4 ГБ при работе в 64-разрядной Windows. Это даст вам значительный объем памяти над адресным пространством, обычно используемым процессом.

1 голос
/ 05 декабря 2010

Если возможно, я бы использовал потоковый подход и использовал бы в комбинации только XML-парсер, работающий только в прямом направлении, что также должно повысить производительность.

Если вам абсолютно не нужно использовать WCF, вы можете написать свой собственный HttpRequest, а затем передать ответ в XmlDeserializer и затем проанализировать ответ, как этот. Это может дать вам больше контроля и понимания того, где на самом деле возникает проблема. Вы также можете поэкспериментировать с фиктивной службой, которая возвращает очень большие документы того типа, который вы ищете. У нас было много головных болей с фрагментацией LOH, поэтому я действительно чувствую вашу боль.

Проблема, которую я заметил, когда строение буферов .NET имеет тенденцию удваивать емкость каждый раз, когда буфер заполняется, что вызывает фрагментацию памяти, поскольку для документа размером 10 МБ память должна быть распределена в много шагов Если вы заранее знаете необходимый размер буфера, более эффективно выделить его сразу. Поэтому, если вы знаете, насколько большим будет входящий документ, вы можете создать StringBuilder именно с таким размером.

1 голос
/ 03 июня 2010

Я думаю, что ваша проблема Может быть утечкой сборки, вызванной использованием XmlSerializer и не использованием одного из двух конструкторов, как указано в этой статье MSDN :

Для повышения производительности инфраструктура XML-сериализации динамически генерирует сборки для сериализации и десериализации указанных типов.Инфраструктура находит и использует эти сборки.Это происходит только при использовании следующих конструкторов:

XmlSerializer.XmlSerializer (Type)

XmlSerializer.XmlSerializer (Type, String)

Если вы используете любой из других конструкторовнесколько версий одной и той же сборки генерируются и никогда не выгружаются, , что приводит к утечке памяти и снижению производительности.

Хорошо, да.Ответ заключается в кэшировании вашего XmlSerializer (при условии, что вы его даже создадите).

Чтобы действительно понять это, вам нужно сделать то, что Tess скажет вам сделать.Она чертовски гений.

...