2011-09-11 6 views
7

Ho un'applicazione .NET 3,5Elaborazione di stringhe grandi, questa frammentazione di heap di oggetti di grandi dimensioni?

  • Una funzione è in esecuzione un milione di volte
  • Sta facendo ricerca & sostituire & operazioni regex nelle stringhe 1MB + (stringhe di dimensioni diverse)

Quando il profiling l'applicazione posso confermare che queste stringhe sono memorizzate in LOH ma in seguito vengono recuperate da GC, quindi in un dato momento solo 10 di esse sono in LOH (10 thread è in esecuzione).

La mia comprensione è, questi grandi archi si trovano a LOH, ottenendo quindi riutilizzato per GC, ma eppure in qualche modo a causa delle loro posizioni di allocazione (e di essere in LOH in modo da non ottenere compattato) questo fa sì che la frammentazione. Questo sta accadendo nonostante non ci siano perdite di memoria nell'operazione.

Non causa un problema in ~ 100K volte, tuttavia quando raggiunge 1 M + fornisce eccezioni di memoria insufficiente.

sto usando ANTS Profiler di memoria e questo è il risultato che ho ottenuto nei primi esecuzioni:

.NET Using 70MB of 210MB total private bytes allocated in to the application 
Number of Fragments: 59 
Number of Large Fragments : 48 (99.6% of free memory) 
Largest Fragment: 9MB 
Free Space: 52% of total memory (37MB) 
Unmanaged Memory: 66% of total private memory (160MB) 
  1. Pensi che la mia diagnosi sono corrette in base ai dati in mano?
  2. Se è così, come posso risolvere questo problema di frammentazione LOH? Devo elaborare quelle stringhe e sono grandi stringhe. Dovrei trovare un modo per dividerli e lavorare in quel modo? In quel caso l'esecuzione di espressioni regolari, ecc. In stringhe divise, sarà davvero difficile.
+1

Un'altra possibile soluzione: fare un processo separato che fa la manipolazione di stringhe, e utilizzare un nuovo processo per ogni stringa (o ogni 100K se che sembra bene per voi le stringhe, ecc). Ogni processo inizia con una lavagna pulita. Questo è uno dei motivi per cui IIS ricicla i pool di app: la frammentazione. – vcsjones

+0

@vcsjones, ci ho pensato prima che lo realizzassi e lo eccessi :) Voglio essere sicuro che questa sia la causa. Sono nuovo per i dettagli di GC, quindi non voglio ore e più tardi per scoprire che in realtà non ho risolto nulla! Il problema è riprodurre il problema reale è piuttosto difficile, potrebbe essere necessario un giorno o due, se sono fortunato. Quindi prendo la parola di profiler per la maggior parte del tempo. –

+2

Si potrebbe provare a eseguire il programma in modalità 64 bit. Questo risolverebbe il problema perché lo spazio virtuale è molto più grande. – xanatos

risposta

2
  1. Sì. Sembra corretto Il LOH si sta frammentando, il che fa sì che il runtime non sia in grado di allocare abbastanza spazio contiguo per le stringhe di grandi dimensioni.

  2. Hai alcune opzioni, suppongo che fare che sia sempre più semplice ed efficace è quella che dovresti scegliere. Tutto dipende interamente da come è scritto.

    1. rompere il vostro stringhe in piccoli pezzi sufficiente che essi non sono nel LOH. (meno di 85K - Nota: la logica per quando un oggetto viene inserito nel LOH isn't that cut-and-dry.) Ciò consentirà al GC di poter recuperare lo spazio. Questo non è assolutamente garantito per correggere la frammentazione - può sicuramente accadere altrimenti. Se rendi le stringhe più piccole, ma finisci comunque sul LOH, rimanderai il problema. Dipende da quanto più di 1 milione di stringhe è necessario gestire. L'altro lato negativo è - devi ancora caricare la stringa in memoria per dividerla, quindi finisce sul LOH comunque. Avrai il restringimento delle corde prima del, la tua applicazione li carica anche. Una specie di Catch-22. EDIT: Gabe nei commenti evidenzia che se è possibile caricare prima la stringa in un StringBuilder, sotto le copertine fa un buon sforzo per tenere le cose fuori dalla LOH (finché non si chiama ToString su di esso).

    2. Interrompere l'elaborazione della stringa in un processo separato. Utilizzare un processo anziché un thread. Usa ogni processo per elaborare dire, stringhe 10K, quindi uccidere il processo e avviarne un altro. In questo modo, ogni processo inizia con una lavagna pulita.Il vantaggio di ciò è che non cambia la logica di elaborazione delle stringhe (nel caso in cui non sia possibile rendere le stringhe più piccole per l'elaborazione) ed evita il catch-22 in # 1. Il rovescio della medaglia è che richiede probabilmente un cambiamento più grande per l'applicazione e il coordinamento del lavoro tra il processo master e il processo di elaborazione slave. Il trucco è che il maestro può solo dire dove è la stringa grande, non può darglielo direttamente, altrimenti si torna al catch-22.

+2

In generale puoi probabilmente caricare la tua stringa in un 'StringBuilder' che (almeno in .NET 4.0) è molto attento a evitare il LOH. Ciò renderebbe facile rompere le corde. Sfortunatamente, non è possibile eseguire un Regex su un 'StringBuilder' e potrebbe non essere possibile eseguire il Regex di cui ha bisogno su stringhe spezzate. – Gabe

+0

@Gabe - Questo è un ottimo punto. – vcsjones