Как я могу очистить PooledBufferManager для моего используемого однажды HTTP-клиента WCF?
У меня есть клиентское приложение WCF, которое выполняет один вызов службы с очень большим ответом (1 ГБ). Я считаю, что для выполнения этого вызова службы требуется много памяти (500 МБ), которая, кажется, никогда не будет восстановлена, даже если на объекты ответа больше не ссылается мой код.
Я использовал профилировщик памяти, чтобы видеть, что большая часть использования памяти лежит в байтовых массивах, созданных экземплярами PooledBufferManager.
Используемый мной прокси / клиент был автоматически сгенерирован Visual Studio, поэтому это класс, производный от System.ServiceModel.ClientBase
Я использую пользовательскую привязку со следующей конфигурацией:
<customBinding>
<binding name="foo"
closeTimeout="00:01:00"
openTimeout="00:01:00"
receiveTimeout="00:01:00"
sendTimeout="00:01:00">
<transactionFlow/>
<reliableSession ordered="true" inactivityTimeout="00:02:00"/>
<security authenticationMode="SecureConversation" messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10">
<secureConversationBootstrap messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10">
<localClientSettings maxClockSkew="23:59:00"/>
</secureConversationBootstrap>
<localClientSettings maxClockSkew="23:59:00"/>
</security>
<mtomMessageEncoding>
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647"/>
</mtomMessageEncoding>
<httpTransport maxBufferPoolSize="2147483647" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647"/>
</binding>
</customBinding>
Читая вокруг, люди говорят о нескольких решениях этого:
- переключение в режим потокового ответа вместо буферизованного, но я считаю, что это для TCP WCF-соединений, а не для HTTP, как у меня.
- установка maxBufferPoolSize в ноль, чтобы отключить пулы буферов. Установка этого в элементе httpTransport, кажется, не имеет никакого эффекта для меня.
- вызов Clear() для соответствующего BufferManager/PooledBufferManager. Я не могу найти объект для вызова этого из контекста производного экземпляра ClientBase, который у меня есть. Мне удалось найти соответствующий экземпляр PooledBufferManager, используя отладчик и копая многие уровни вглубь частных полей innerChannelFactory, но это бесполезно для взаимодействия с ним из кода.
- вручную вызывая GC.Collect(), похоже, возвращает около 50 МБ из выдающихся 500 МБ.
Каков наилучший способ вернуть как можно больше этой памяти, используемой этим одноразовым сервисным вызовом? Я нахожусь на грани создания сервисного вызова в выделенном процессе, который я могу убить, чтобы восстановить память на этом этапе.
1 ответ
- переключение в режим потокового ответа вместо буферизованного, но я считаю, что это для TCP WCF-соединений, а не для HTTP, как у меня.
Даже с http вы можете переключиться в потоковый режим, как я сделал для https, но http работает так же хорошо.
Просто добавьте transferMode="Streamed"
приписать к вашему httpTransport
элемент. Поскольку вы беспокоитесь о клиенте, вы должны сделать это в app.config
вашего клиента. (Вы также можете сделать это независимо в файле web.config сервера, если вы также хотите изменить сервер на streamed
Режим. Но не нужно менять как клиента, так и сервера, режим передачи не меняет байты на проводе)
- установка maxBufferPoolSize в ноль, чтобы отключить пулы буферов. Установка этого в элементе httpTransport, кажется, не имеет никакого эффекта для меня.
Это именно то, что, как утверждает эта статья, должно работать:
Если maxBufferPoolSize = 0, то создается GCBufferManager, в противном случае вы получите PooledBufferManager. Первый из них тривиален, и на самом деле он вообще не выполняет никакого управления, а просто выделяет новый буфер для любого запроса и позволяет сборщику мусора позаботиться об его удалении.
то есть руководство GC.Collect()
может на самом деле сделать свое дело в этом случае.
- вызов Clear() для соответствующего BufferManager/PooledBufferManager. Я не могу найти объект для вызова этого из контекста производного экземпляра ClientBase, который у меня есть.
Я не смог получить доступ к этому экземпляру BufferManager, когда я пытался это сделать.
- вручную вызывая GC.Collect(), похоже, возвращает около 50 МБ из выдающихся 500 МБ.
Не будет работать для диспетчера буферных пулов, так как он никогда не освободит буфер после его создания, если вы не вызовете Clear()
, что вы не можете, без указателя на экземпляр.