2010-06-22 16 views
12

Esecuzione di un'applicazione .NET su Windows Server 2008 x64 con 16 GB di RAM. Questa applicazione deve recuperare e analizzare una grande quantità di dati (circa 64 GB) e conservarli tutti in memoria contemporaneamente.Garbage collector .NET e memoria virtuale x64

Cosa mi aspetto di vedere: le dimensioni del processo si espandono da 16 GB a 64 GB. Windows utilizza la memoria virtuale per inviare i dati aggiuntivi al/dal disco, se necessario. Questo è il classico caso d'uso della memoria virtuale.

Cosa vedo realmente: la dimensione del processo è limitata alla quantità di memoria fisica (16 GB). L'applicazione spende il 99,8% del suo tempo nel garbage collector.

Perché la nostra applicazione non riesce a utilizzare la memoria virtuale? Si tratta di un problema nella configurazione del garbage collector .NET o nel gestore della memoria virtuale x64 di Windows stesso? Cosa posso fare per far sì che la nostra applicazione utilizzi la memoria virtuale anziché essere limitata alla memoria fisica?

Grazie.

- Brian

Aggiornamento: Ho scritto un piccolo programma che presenta lo stesso comportamento:

using System; 

namespace GCTest 
{ 
    class Program 
    { 
     static void Main() 
     { 
      byte[][] arrays = new byte[100000000][]; 
      for (int i = 0; i < arrays.Length; ++i) 
      { 
       arrays[i] = new byte[320]; 
       if (i % 100000 == 0) 
       { 
        Console.WriteLine("{0} arrays allocated", i); 
        System.Threading.Thread.Sleep(100); 
       } 
      } 
     } 
    } 
} 

Se volete provarlo, assicuratevi di costruire per x64. Potrebbe essere necessario modificare leggermente le costanti per sottolineare il sistema. Il comportamento che vedo è che il processo si blocca quando si avvicina a una dimensione di 16 GB. Non c'è nessun messaggio di errore o eccezione lanciata. Il monitor delle prestazioni segnala che la% del tempo della CPU in GC si avvicina al 100%.

Non è inaccettabile? Dov'è il sistema di memoria virtuale?

+0

Che cosa state usando per determinare la dimensione del processo? (Eliminiamo prima le opzioni semplici :)) – Paolo

+0

"Commit size" dal Task Manager di Windows. Utilizza anche "# byte impegnati totali" in Performance Monitor. Sono abbastanza sicuro che sto misurando le dimensioni della memoria virtuale del processo piuttosto che il suo set di lavoro fisico. – brianberns

+0

Posso chiedere l'ovvio? Hai davvero bisogno di caricare tutto questo in memoria in una volta? È possibile prendere un altro approccio e fare una sorta di paging manuale da solo? – CodingGorilla

risposta

2

Sembra che tu non stia mantenendo un riferimento ai dati di grandi dimensioni. Il garbage collector non raccoglierà oggetti di riferimento.

+0

Non penso sia rilevante. Nessuno dei dati è in realtà spazzatura: è tutto referenziato. Pertanto, non mi aspetto che il GC raccolga alcun ricordo. Sembra che il GC stia consumando il 100% della CPU perché il processo ha esaurito la memoria fisica e .NET sta cercando di liberarsene.Tuttavia, non c'è nulla da liberare - idealmente, il processo dovrebbe semplicemente espandersi usando la memoria virtuale invece di spendere tutto il suo tempo nel GC cercando di liberare oggetti inesistenti (non esistenti). – brianberns

+0

Se si stanno allocando 64 GB di dati, ma solo 16 GB vengono allocati, il resto non viene referenziato ed è stato sottoposto a GC. A meno che non capisco la tua domanda. –

+0

Non stai capendo la domanda. Sto cercando di allocare 64 GB di dati, ma il processo si blocca dopo aver assegnato solo 16 GB di dati. Non ho mai la possibilità di allocare il resto dei dati. – brianberns

10

Hai controllato per assicurarti che il tuo file di paging sia configurato in modo che possa espandersi a quella dimensione?

Aggiornamento

Ho giocato in giro con questo un po 'con il dato esempio, e qui è quello che vedo.

Sistema: Windows 7 a 64 bit, 6 GB di RAM a tre canali, 8 core.

  1. È necessario un file di paging aggiuntivo su un altro mandrino dal sistema operativo o questo tipo di analisi ridurrà la macchina. Se tutto combatte sullo stesso file di paging, peggiora le cose.

  2. Sto vedendo una grande quantità di dati promossi di generazione in generazione nel GC, oltre a un gran numero di sweep \ raccolte GC e una quantità massiccia di errori di pagina come risultato del raggiungimento dei limiti di memoria fisica. Posso solo supporre che quando la memoria fisica è esaurita \ molto alta, ciò innesca sweep e promozioni generando così una grande quantità di memoria sfasata da toccare che sta portando a uno sprirale della morte quando la memoria toccata viene cercata e altra memoria è costretto a uscire. Il tutto finisce in un pasticcio fradicio. Ciò sembra essere inevitabile quando si assegna un numero elevato di oggetti longevi che finiscono nell'Heap di piccoli oggetti.

Ora confrontare questo a destinare gli oggetti in modo assegnerà direttamente nel mucchio Large Object (che non soffre gli stessi problemi di spazzamento e di promozione):

private static void Main() 
{ 
    const int MaxNodeCount = 100000000; 
    const int LargeObjectSize = (85 * 1000); 

    LinkedList<byte[]> list = new LinkedList<byte[]>(); 

    for (long i = 0; i < MaxNodeCount; ++i) 
    { 
     list.AddLast(new byte[LargeObjectSize]); 

     if (i % 100000 == 0) 
     { 
      Console.WriteLine("{0:N0} 'approx' extra bytes allocated.", 
       ((i + 1) * LargeObjectSize)); 
     } 
    } 
} 

Questo funziona come ad esempio previsto la memoria virtuale viene utilizzata e poi esaurita - 54 GB nel mio ambiente \ configurazione.

Quindi sembra che l'allocazione di una massa di piccoli oggetti di lunga durata porterà infine a un circolo vizioso nel GC mentre le spazzate di generazione e le promozioni vengono fatte quando la memoria fisica si è esaurita - è una spirale mortale di file di pagine.

Update 2

Mentre indagando la questione ho giocato con una serie di opzioni \ configurazioni che ha fatto alcuna differenza apprezzabile:

  • modalità GC Server forzatura.
  • Configurazione GC a bassa latenza.
  • Varie combinazioni di forzatura del GC per provare ad ammortizzare GC.
  • Min \ max set di lavoro del processo.
+0

No, ma il gestore della memoria virtuale di Windows non dovrebbe avviare automaticamente il paging, almeno fino a un certo grado dopo 16 GB? Qual è il punto di avere memoria virtuale se è limitata dalla quantità di memoria fisica? – brianberns

+0

È configurabile, vale la pena controllare. I problemi potrebbero variare da una politica inattesa, ad alcune scintille luminose che disattivano la disattivazione. –

+0

OK, ho aumentato il file di paging a 30 GB, ma non ha avuto alcun effetto. Penso che il garbage collector di .NET mi stia impedendo di allocare più di 16 GB di oggetti, quindi il sistema di memoria virtuale di Windows non ha mai la possibilità di avviare il paging. – brianberns