2015-03-04 18 views
8

Alcuni dati: Abbiamo sviluppato il servizio wcf che funge da livello tra i client e il database. È selfhosted e funziona come un servizio Windows..NET Garbagecollector trouble. Blocchi per 15-40 minuti

Il servizio mantiene diverse cache, di cui le più grandi sono circa da 1 GB in memoria. L'utilizzo totale della memoria è in genere di circa 5-8 GB. Le connessioni sono duplex e utilizzano il protocollo tcp e la serializzazione viene effettuata con protobuf-net. Il numero di client connessi di solito varia da 1000 a 1500. Il server è un xeon 8-core di un modello nuovo con 64 GB di memoria e non esegue nient'altro che il servizio.

Il problema: dopo x quantità di tempo, è passato da un giorno a una settimana il servizio diventa estremamente lento. Le richieste che richiedono 0,5 secondi possono richiedere più di un minuto. Questo comportamento va avanti per 15-40 minuti o fino al riavvio del servizio.

Cosa abbiamo fatto: Abbiamo controllato la rete e la connessione di rete al server e non ci sono problemi. L'utilizzo della CPU aumenta leggermente durante questo periodo da f.eks. Media del 30% al 40-50% Abbiamo preso i dump della memoria e non ci sono blocchi logici nel codice che blocca gli utenti e non molta attività. Il nostro ultimo vantaggio è il Garbage collector. In perfmon possiamo vedere che "% time in gc" è costantemente superiore al 90% (90-97%) e il conteggio delle raccolte aumenta. Sia GC0 che GC1. Sospettiamo che sia in esecuzione anche un GC2 di blocco, ma abbiamo dovuto riavviare il servizio poiché questo è in produzione, quindi non ha eseguito il conteggio durante la finestra 5min che abbiamo eseguito perfmon. L'utilizzo della memoria era di 7,6 Gb. Nota: le chiamate in sospeso aumentano, pertanto le chiamate arrivano ma il servizio non le gestisce.

Le mie domande sono, il garbage collector può entrare in uno stato in cui gira e blocca costantemente per oltre 15 minuti? o il problema è probabilmente legato ad un altro problema?

Il nostro servizio ha eseguito GC in modalità workstation e latencymode: Interactive Ora lo abbiamo modificato su Server e SustainedLowLatency e spero che questo sia di aiuto. C'è qualcos'altro che possiamo fare se è il netturbino?

Modifica: l'utilizzo della memoria di grandi dimensioni è in corso di progettazione, i dati nella cache sono così grandi e c'è molta più memoria disponibile.

+0

Suggerisci di scoprire la causa principale dell'elevato utilizzo della memoria ... ad es. prova ad aggiungere il blocco "using" per liberare memoria una volta finito usando quell'oggetto – User2012384

+0

Solo per curiosità, quanti thread hai? Controlla il Task Manager. Almeno alcuni anni fa c'era il problema che più thread avevi (anche inattivo), più lentamente GC – xanatos

+2

"Il garbage collector può entrare in uno stato in cui viene eseguito e blocca costantemente per oltre 15 minuti"? Assolutamente, se ha costantemente bisogno di liberare memoria ma non è in grado di farlo, perché continui a tenerti stretto. Come ha detto Raymond Chen, "una cache con una cattiva politica è un altro nome per una perdita di memoria". –

risposta

4

L'eccessiva garbage collection è spesso causata da problemi di codice. Si creano troppi oggetti in poco tempo oppure si continua a allocare memoria senza rilasciarla.

C'è in realtà un extensive checklist available on MSDN che dovrebbe aiutare a diagnosticare il problema.

Un GC2 molto grande significa che gli oggetti presenti sono sopravvissuti a più raccolte di dati inutili, il che significa che vengono conservati in memoria per un periodo di tempo più lungo. Questa potrebbe essere la causa principale del tuo problema. Forse esiste un meccanismo di memorizzazione nella cache che potrebbe utilizzare un criterio di ottimizzazione/conservazione (rimuovere i dati che non vengono utilizzati per un lungo periodo di tempo).

+0

La nostra cache più grande è più o meno una collezione immutabile, aggiunge dati quando richiesto e mancante. L'articolo ha quindi una durata di 4 ore se non si accede nuovamente, quindi il contatore viene resettato. Quindi è piuttosto semplice. Uno scenario comune è che cresce fino a 1-1,5 GB all'inizio della giornata. quindi aggiunge 0,5 gb di nuovi dati e rimuove 0,5 gb e di notte è completamente cancellato. Se è la cache che è il problema vorrei poter dire al gc di non toccarlo affatto durante il giorno e scansionarlo di notte. Riceverò questo comportamento se avrò impostato Sustainedlowlatency e quindi forzerò un gc.collect di notte? –

+0

Johan: se lo si imposta, il GC verrà ancora più frequente di una volta al giorno. GC è un processo continuo. Nota la sezione commenti su ['GCLatencyMode'] (https://msdn.microsoft.com/en-us/library/system.runtime.gclatencymode%28v=vs.110%29.aspx): "Le raccolte di blocchi completi possono ancora verificarsi se il sistema è sotto pressione di memoria." –

+0

Se mi piacerebbe avere una grande cache persistente in .net. Come dovrei quindi implementarlo in modo che il GC non rovini le mie prestazioni? Voglio dirlo quando controllare? CLR hosting con retain o qualcosa del genere? –

0

Ho una situazione simile. Cache di dati di database di grandi dimensioni in un servizio che utilizza protobuf con WCF per la comunicazione client. La cache non è puramente solo per i client, il livello aziendale utilizza la cache per eseguire operazioni. L'ingombro di memoria del servizio può essere compreso tra 2 e 10 GB. Rilascio un segmento della cache dopo 8 ore di inattività. La macchina ha 8 core virtuali e 32 GB di memoria. Sto usando .Net 4.5.1.

Il GC consumerebbe il 98% della CPU per un'ora non appena ho caricato la cache dal database. Il punto interessante qui in entrambi i nostri casi non è la pressione della memoria che cosa mai.

Penso che il GC sia eseguito indipendentemente dal fatto che qualcosa è stato cambiato dove il GC cerca di mantenere la memoria disponibile per tutti i thread. Dato che un thread ha allocato una grande quantità di memoria durante il caricamento della cache, il GC ha dato il via. Ho dovuto fare diverse cose per risolverlo.

1) Tuple rimosse dalla cache. Li stavo usando come chiavi del dizionario e la loro implementazione di StructuralEquality è orribile. Confronta tutte le proprietà come oggetti, quindi c'è un sacco di boxing in corso per le proprietà che sono valori e questi dovranno essere raccolti con garbage ad un certo punto.

2) Quando si sostituivano Tuple utilizzate come chiavi, non potevo semplicemente sostituirle con strutture senza l'implementazione di Equals poiché il confronto di valori utilizza il reflection ed è troppo costoso quindi ho finito con la creazione di una struttura di coppia generica. Ho deciso di utilizzare le strutture per rimuovere il numero di oggetti quando erano negli array.

3) Per rimuovere le tuple, ho dovuto creare la mia struttura di paia che confronta le proprietà utilizzando l'equazione predefinita per i tipi di proprietà. Esattamente la stessa cosa creata da PowerCollections.