3 votes

Comment puis-je effacer le PooledBufferManager pour mon client WCF HTTP utilisé une seule fois ?

J'ai une application client WCF qui fait un seul appel de service avec une réponse très importante (1GB). Je constate que cet appel de service utilise beaucoup de mémoire (500 Mo) qui semble ne jamais être récupérée, même si les objets de la réponse ne sont plus référencés par mon code.

J'ai utilisé un profileur de mémoire pour voir qu'une grande partie de l'utilisation de la mémoire se trouve dans les tableaux d'octets créés par les instances de PooledBufferManager.

Le proxy/client que j'utilise a été généré automatiquement par Visual Studio, il s'agit donc d'une classe dérivée de System.ServiceModel.ClientBase< TChannel >.

J'utilise une liaison personnalisée avec la configuration suivante :

  <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>

En lisant autour de moi, les gens parlent de quelques solutions à ce problème :

  • le passage au mode de réponse en continu plutôt qu'en mémoire tampon, mais je crois que c'est pour les connexions TCP WCF, et non pour HTTP comme la mienne.
  • la définition de maxBufferPoolSize à zéro pour désactiver les pools de tampons. Ce réglage dans l'élément httpTransport ne semble pas avoir d'effet pour moi.
  • en appelant Clear() sur le BufferManager/PooledBufferManager approprié. Je n'arrive pas à trouver l'objet sur lequel appeler cela dans le contexte de l'instance dérivée ClientBase que j'ai. J'ai réussi à trouver l'instance PooledBufferManager appropriée en utilisant le débogueur et en creusant à plusieurs niveaux dans les champs privés innerChannelFactory, mais cela n'est pas utile pour interagir avec elle à partir du code.
  • l'invocation manuelle de GC.Collect() semble récupérer environ 50 Mo sur les 500 Mo restants.

Quel serait le meilleur moyen de récupérer le maximum de la mémoire utilisée par cet appel de service unique ? Je suis sur le point de faire l'appel de service dans un processus dédié que je peux tuer pour récupérer la mémoire à ce stade.

2voto

Eugene Beresovksy Points 3852
  • le passage au mode de réponse en continu plutôt qu'en mémoire tampon, mais je crois que c'est pour les connexions TCP WCF, et non pour HTTP comme la mienne.

Même avec http, vous pouvez en fait passer en mode streaming comme je l'ai fait pour https mais http travaille juste ainsi que .

Il suffit d'ajouter un transferMode="Streamed" à l'attribut httpTransport élément. Comme vous vous préoccupez du client, vous devez le faire dans l'élément app.config de votre client. (Vous pouvez également le faire indépendamment dans le web.config du serveur, si vous voulez également changer le serveur en streamed mode. Mais il n'est pas nécessaire de changer le client et le serveur, le mode de transfert ne change pas les octets sur le fil).

  • la définition de maxBufferPoolSize à zéro pour désactiver les pools de tampons. Ce réglage dans l'élément httpTransport ne semble pas avoir d'effet pour moi.

Ce qui est exactement ce que cet article devraient fonctionner :

Si maxBufferPoolSize = 0, un GCBufferManager est créé, sinon vous obtenez un PooledBufferManager. Le premier est trivial, et en fait ne fait aucune gestion, mais alloue simplement un nouveau tampon pour toute requête, et laisse le gestionnaire de tampon

c'est-à-dire un manuel GC.Collect() pourrait faire l'affaire dans ce cas.

  • en appelant Clear() sur le BufferManager/PooledBufferManager approprié. Je n'arrive pas à trouver l'objet sur lequel appeler cela dans le contexte de l'instance dérivée ClientBase que j'ai.

Je n'ai pas non plus pu accéder à l'instance de BufferManager lorsque j'ai essayé de le faire.

  • l'invocation manuelle de GC.Collect() semble récupérer environ 50 Mo sur les 500 Mo restants.

Cela ne fonctionnera pas pour le gestionnaire de tampon commun, car il ne libérera jamais un tampon une fois qu'il aura été créé, à moins que vous n'appeliez Clear() ce qui est impossible en l'absence d'un pointeur sur l'instance.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X