2009-05-07 19 views
6

Il problema è semplice Ho un processo, che fa ETL su alcuni file xml. Abbiamo iniziato a ottenere file xml veramente grandi e ho iniziato a ricevere OutOfMemoryExceptions.Come eseguire un processo .NET esaurito la memoria senza esaurire tutta la memoria di sistema

La riparazione del processo è relativamente semplice. Tuttavia, mi piacerebbe fare un test unitario per la mia suite NUnit per assicurarmi che il processo continui a essere in grado di gestire file veramente grandi. Tuttavia, l'esaurimento della memoria nella mia workstation di sviluppo rallenta la mia macchina e richiede molto tempo. Anche memorizzare un file di test gigantesco nel controllo della versione è una cattiva idea. Se potessi limitare artificialmente un processo, thread o appdomain per usare solo una quantità fissa di ram, diciamo 128 meg, potrei fare un test di unità più piccolo che non porterebbe la mia workstation in ginocchio.

Qualche suggerimento? La loro qualche API non gestita posso P/Invoke?

+0

Non volevo mettere questo nella mia risposta principale, perché non è esattamente rispondere alla tua domanda, ma questo suona proprio come una cosa strana per unit test. Ad un livello elevato, il tuo algoritmo per la lettura dei file gestirà file molto grandi di dimensioni arbitrarie (perché flussi o blocchi il file) o non lo farà. Questo non cambierà molto spesso, se mai, e non vedo cosa guadagni provandolo ogni volta con la tua suite di test, specialmente dato che (se fallisce) potrebbe non fallire in modo affidabile ogni volta su un dato file. – mquander

+0

Apro e scrivo il file più volte. A volte lo faccio come un flusso, a volte no. Il 99,9% dei file che scrivo sono abbastanza piccoli da poter caricare l'intero file in memoria. L'altro 0,01% deve essere elaborato. Pertanto, se faccio in modo che tutto il flusso delle operazioni dei file sia basato (risolvendo il problema), qualcuno potrebbe aggiungere un nuovo passaggio che elabora il file caricandolo tutto in memoria. Senza un test unitario che esegue l'intera operazione ETL su un file abbastanza grande da occupare tutta la ram di elaborazione, questo lo renderà in produzione, perché il QA potrebbe non testare il sistema con un file molto grande. –

+0

Penso che la migliore risposta qui sia quella di incapsulare le operazioni che devi fare molto bene, in modo che sia difficile per chiunque accidentalmente andare lungo il percorso di ottenere l'handle del file e aprire l'intera cosa per cercare di recuperare qualcosa. Certo, qualcuno potrebbe ancora andare e hackerare nella tua giungla per aprire l'intero file se si fossero sforzati abbastanza, ma penso che potresti impedire a chiunque di farlo per pura ignoranza. (So ​​che questo è accessorio alla discussione su come testarlo, ma la prevenzione è la migliore cura.) – mquander

risposta

0

Non capisco bene cosa stai ricevendo qui. Supponiamo che in qualche modo tu abbia creato un "ambiente di prova" artificialmente piccolo che non è in grado di allocare pezzi di memoria così grandi, quindi ha lanciato OutOfMemoryExceptions su file più piccoli. Cosa hai guadagnato? Il test unitario doveva verificare se è possibile gestire file più grandi su un sistema reale, giusto?

L'elemento importante è presumibilmente che si possano gestire file "grandi quanto devono essere" su qualsiasi sistema su cui verranno eseguiti, e non esiste un vero metodo per testare l'unità al di fuori di provare quel file su quel sistema.

(Una più piccola, cosa meno importante potrebbe essere se si gestiscono OutOfMemoryException s con grazia, ma non c'è bisogno di correre in realtà di memoria per verificare che basta a rendere il vostro metodo di un'eccezione di tanto in tanto e osservare che fa la cosa giusta.)

+0

Il test è che il sistema può gestire file arbitrariamente grandi, non proprio grandi file. In altre parole, tutte le operazioni sui file vengono eseguite senza caricare l'intero file in memoria e non ci dovrebbero essere limiti sulla quantità di file che il sistema potrebbe gestire. –

2

Non è possibile utilizzare una struttura di simulazione per l'allocazione di memoria e far lanciare OutOfMemoryException come uno dei test?

Detto questo, però, se hai davvero esaurito la memoria, non c'è molto che la tua applicazione possa tranquillamente fare, ma se riesci almeno a fallire con grazia i tuoi utenti saranno grati.

Un esempio: Avevo un caso in un precedente lavoro in cui stavamo visualizzando modelli 3D di fabbriche in tempo reale. I modelli sono cresciuti così tanto che, quando abbiamo provato a caricare le trame, avremmo esaurito i problemi di memoria. Siamo riusciti a mantenere in vita l'applicazione e il rendering assicurandoci che il codice gestisse i puntatori nulli anche se il resto del codice pensava che ci fossero informazioni sulla trama.

+0

Stavo solo per suggerire la stessa cosa per gli oggetti finti. – RichardOD

1

Mocking è la cosa migliore. In realtà, l'aumento di una OOM è defininition non un test unitario. Quando si ha a che fare con la memoria, si ha a che fare con il test di carico. Se leggi i collegamenti in fondo a questa email, troverai che le OOM reali sono terribilmente difficili da riprodurre e correggere nel migliore dei casi. Un'eccezione OOM forzata non è la vera causa dell'eccezione e quindi non è più interessante di una simulazione di test.

Attaccare con un test dell'unità utilizzando un simulato per la convalida. Se ottieni ancora OOM, getti più memoria sul tuo server e fai riciclare/riavviare il processo più spesso.

Ecco alcune letture interessanti su OutMemoryExceptions che ho raccolto l'ultima volta che ho combattuto con loro. Riepilogo: Le OOM si verificano quando il sistema non è in grado di allocare l'importo richiesto, il che non significa che la memoria è esaurita.

+0

"Se ottieni ancora OOM, carica più memoria sul server e riavvia il processo più spesso." L'aggiunta di più memoria non influisce sul problema ... sta esaurendo la memoria virtuale nel processo. La memoria fisica non è il problema. Supponendo che si stia eseguendo su una piattaforma a 32 bit, si dispone di 2 GB per lo spazio degli indirizzi dell'applicazione che è possibile allocare da entrambi gli heap e lo stack. OOM si verificano quando non è possibile allocare memoria sufficiente contigua nello spazio indirizzo. –

0

E 'abbastanza facile da provocare fuori eccezioni di memoria in un processo.

Basta creare un ciclo che allochi la memoria in blocchi abbastanza piccoli da non essere sull'heap di oggetti di grandi dimensioni (ma non troppi che si hanno a causare l'eccezione) e quindi si può provare ad aprire un file più piccolo e che causerà l'apertura del file per non essere in grado di allocare memoria contigua sufficiente e otterrete l'eccezione OOM all'apertura del file senza la necessità di un file enorme. Qualcosa di simile ...

List<byte[]> items = new List<byte[]>(); 
for (int i = 0; i < 10000; i++) 
{ 
    byte[] c = new byte[160000]; 
    items.Add(c); 
} 

byte[] next = new byte[1000000000]; 

Se si esegue il codice di cui sopra come è, si otterrà un'eccezione OOM sull'ultima riga. Ma se commentate prima il ciclo, verrà eseguito senza errori. Probabilmente dovrai modificare un po 'il ciclo per farlo fallire ogni volta, ma puoi farlo. Basta eseguire il ciclo prima della chiamata per aprire il file nel test e avrete esaurito una grande porzione di memoria e l'apertura non dovrebbe riuscire.

Inoltre, si potrebbe voler impostare l'opzione/3GB se è un'opzione per voi. Non è sempre la risposta giusta e ha degli svantaggi associati, ma cambia la forma della memoria virtuale da 2 GB/2 GB a 1 GB/3 GB consentendo al tuo processo di accedere a più spazi di indirizzi virtuali. Questo ti darà un po 'più di respiro nella dimensione dei file che puoi aprire. Ancora una volta, dovresti leggere gli aspetti negativi di fare ciò prima di andare dopo questa come una soluzione e assicurarti che ne valga la pena se aiuta la tua situazione.

Here è come attivare sul server

Problemi correlati