2010-04-22 5 views
5

La situazione è che sto effettuando una chiamata WCF a un server remoto che restituisce un documento XML come stringa.Maxmise il più grande blocco di memoria contiguo nell'heap a oggetti grandi

Il più delle volte questo valore di ritorno è a pochi K, a volte qualche decina K, molto occasionalmente qualche centinaio di K, ma molto raramente potrebbe essere diversi megabyte (primo problema è che non c'è modo per me sapere).

Queste rare occasioni causano dolore. Ottengo una traccia dello stack che inizia:

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) 

ho letto in giro ed è perché il Large Object Heap è solo diventando troppo frammentato, quindi, anche la chiamata con un rapido controllo per StringBuilder.EnsureCapacity solo fa sì che il OutOfMemoryException deve essere lanciata prima (e poiché sto indovinando ciò che è necessario, potrebbe non aver bisogno di così tanto che il mio controllo sta causando più problemi di quello che sta risolvendo). Alcuni opinions sono che non c'è molto che posso fare al riguardo.

Alcune delle domande che mi sono chiesto:

  • Usa meno memoria - Hai controllato la presenza di perdite? Sì. L'utilizzo della memoria va su e giù, ma non c'è una crescita fondamentale che garantisca che ciò accada. Alcune volte fallisce, ha avuto successo in quella fase in precedenza.
  • trasferimento piccole quantità Non è un'opzione, questo è un servizio di terzi web su cui non ho alcun controllo (o almeno ci sarebbe voluto molto tempo per risolvere, nel frattempo ho ancora un problema)
  • Puoi fare qualcosa al LOH per renderlo meno probabile che fallisca? ... ora questo è il corso più fruttuoso. È un processo a 32 bit (deve essere per vari motivi politici, tecnici e noiosi), ma normalmente ci sono centinaia di meg free (multipli dell'importo maggiore per il quale abbiamo visto fallimenti).
  • È possibile monitorare il LOH? Uso perfmon Posso tenere traccia delle dimensioni degli heap, ma non penso che ci sia un modo per monitorare il più grande blocco di memoria contiguo disponibile.

La domanda è: qualche consiglio o suggerimento per le cose da provare?

risposta

5

si potrebbe rivedere il TransferMode proprietà della vostra associazione, per vedere se si soddisfano i requisiti di cambiare dal suo valore predefinito di "Buffered", a "streaming" o "StreamedResponse".

Inoltre, rivedere i valori per maxBufferPoolSize e MaxBufferSize. Aumentare la dimensione dei buffer interni utilizzati può aiutare con l'utilizzo della memoria, in particolare con l'elaborazione di messaggi di grandi dimensioni.

maxReceivedMessageSize probabilmente è già impostato se si ricevono messaggi di grandi dimensioni, ma vorrei rivedere anche quel valore.

Ho visto uno dei valori sopra riportati, se il superamento della soglia, non riesce con un oscuro messaggio relativo alla memoria. L'eccezione originale era effettivamente nascosta dal messaggio che è emerso nella mia applicazione.Abilitare il tracciamento WC2ha aiutato a diagnosticare il problema e vedere l'errore reale - Avevo bisogno di aumentare il valore di uno, o più, delle proprietà di legame sopra.

Non ho avuto la sensazione di legare il tuo utilizzo dal tuo post, ma credo che queste impostazioni siano comuni a quelle principali. Controlla la documentazione MSDN su basicHttpBinding per esempio.

Se è veramente la frammentazione LOH non c'è nulla da fare al riguardo una volta esauriti gli sforzi di messa a punto. Potrebbe essere necessario riciclare il riciclo dell'applicazione per attenuarlo (odio raccomandarlo), ma se hai esaurito altri sforzi potresti rimanere con quello.

+0

Cambiare la rilegatura è una buona idea - darò una prova. Un riciclo a rotazione era la nostra ultima risorsa ... – Unsliced

+0

siamo stati abbastanza fortunati da mitigare qualsiasi problema con l'elaborazione di documenti di grandi dimensioni attraverso entrambi i miglioramenti del codice (possediamo entrambi gli endpoint), o modifiche vincolanti, almeno per farci passare a un servizio di manutenzione periodica programmato (a causa di patch, rilascio di funzionalità, ecc.). L'idea di Steven di utilizzare windbg per l'analisi dell'heap può anche portare benefici. Prova http://blogs.msdn.com/tess/ e troverai ottime informazioni da Tess Ferrandez su come iniziare qui. Laboratori eccellenti! Buona fortuna! –

1

Credo che il problema Potrebbe essere una perdita d'Assemblea ha causato utilizzando XmlSerializer e non utilizzando uno dei due costruttori come indicato in questo MSDN article:

per aumentare le prestazioni, l'XML infrastrutture serializzazione genera dinamicamente gli assembly a serializzano e deserializzano i tipi specificati . L'infrastruttura trova e riutilizza quegli assembly. Questo comportamento si verifica solo quando si utilizza le seguenti costruttori:

XmlSerializer.XmlSerializer (Type)

XmlSerializer.XmlSerializer (Tipo, String)

Se si utilizza uno qualsiasi degli altri costruttori, multipla versioni dello stesso assembly e mai scaricato, che provoca una perdita di memoria e prestazioni insufficienti.

Bello, eh. La risposta è di memorizzare nella cache XmlSerializer (supponendo che tu lo abbia persino creato).

Per capire veramente cosa è necessario fare ciò che Tess ti dice di fare. Lei è un genio bizzarro.

1

Se possibile, opterei per un approccio basato sul flusso e utilizzare un parser forward solo Xml in combinazione, che dovrebbe offrire anche migliori prestazioni.

Se non è assolutamente necessario utilizzare WCF, è possibile scrivere la propria HttpRequest e quindi passare la risposta a XmlDeserializer e quindi analizzare la risposta in questo modo. Potrebbe darti più controllo e informazioni su dove si verifica effettivamente il problema. Puoi anche sperimentare con un servizio di simulazione che restituisce documenti molto voluminosi del tipo che stai cercando. Abbiamo anche avuto molti mal di testa con la frammentazione LOH, quindi sento davvero il tuo dolore.

Un problema che ho notato durante la creazione di buffer .NET tende a raddoppiare la capacità ogni volta che il buffer viene riempito, il che causa la frammentazione della memoria poiché per un documento di dimensione 10mb, la memoria deve essere allocata in molti passaggi. Se si conosce la dimensione del buffer necessaria in anticipo, è più efficiente allocarla contemporaneamente. Quindi, se sai quanto sarà grande il documento in arrivo, puoi creare un StringBuilder con esattamente quella dimensione.

2

Non riesco a risolvere alcuno dei problemi specifici di WCF, ma se è necessario ottimizzare lo spazio LOH per un processo a 32 bit, è necessario rendere l'applicazione large address aware ed eseguirla su 64 bit. Un ampio processo a 32 bit con indirizzo sensibile sarà in grado di indirizzare l'intero spazio di indirizzamento di 4 GB quando viene eseguito su Windows a 64 bit. Questo ti darà un bel po 'di memoria sopra lo spazio degli indirizzi normalmente usato dal processo.