2012-12-12 11 views
19

Sono un po 'confuso sul fatto che possiamo semplicemente prendere uno OutOfMemoryException usando un blocco try/catch.Come funziona il rilevamento di una OutOfMemoryException?

Dato il codice seguente:

Console.WriteLine("Starting"); 

for (int i = 0; i < 10; i++) 
{ 
    try 
    { 
     OutOfMemory(); 
    } 
    catch (Exception exception) 
    { 
     Console.WriteLine(exception.ToString()); 
    } 
} 

try 
{ 
    StackOverflow(); 
} 
catch (Exception exception) 
{ 
    Console.WriteLine(exception.ToString()); 
} 

Console.WriteLine("Done"); 

I metodi che ho usato per creare l'OutOfMemory + StackOverflowException:

public static void OutOfMemory() 
{ 
    List<byte[]> data = new List<byte[]>(1500); 

    while (true) 
    { 
     byte[] buffer = new byte[int.MaxValue/2]; 

     for (int i = 0; i < buffer.Length; i++) 
     { 
      buffer[i] = 255; 
     } 

     data.Add(buffer); 
    } 
} 

static void StackOverflow() 
{ 
    StackOverflow(); 
} 

Esso stampa i OutOfMemoryException 10 volte e quindi termina a causa della StackOverflowException, che non può gestire

Il grafico RAM sembra che durante l'esecuzione del programma: graph showing that memory gets allocated and released 10 times

La mia domanda ora è il motivo per cui siamo in grado di catturare il OutOfMemoryException? Dopo averlo preso, possiamo semplicemente eseguire qualsiasi codice che vogliamo. Come dimostrato dal grafico RAM, viene rilasciata la memoria. Come fa il runtime a sapere quali oggetti può GC e quali sono ancora necessari per ulteriori esecuzioni?

+0

Mostraci il tuo metodo OutOfMemory(). Scommetto che non stai mantenendo un riferimento alla memoria che alloca, quindi il GC può rilasciarlo dopo che è tornato. –

+0

completato, hai ragione – GameScripting

+0

Sì, così come OutOfMemory() genera, tutta la memoria allocata può essere liberata dal GC. –

risposta

24

Il GC esegue un'analisi sui riferimenti utilizzati nel programma e può eliminare qualsiasi oggetto non utilizzato da nessuna parte.

Un OutOfMemoryException non significa che la memoria è completamente esaurita, significa solo che un'allocazione di memoria non è riuscita. Se si è tentato di allocare una grande area di memoria in una volta, potrebbe esserci ancora molta memoria libera residua.

Quando non c'è abbastanza memoria libera per un'allocazione, il sistema esegue una garbage collection per provare a liberare memoria. Se non c'è ancora abbastanza memoria per l'allocazione, verrà generata un'eccezione.

A non è possibile gestire un numero StackOverflowException perché significa che lo stack è pieno e non è possibile rimuovere nulla da esso così come lo è con l'heap. Avresti bisogno di più spazio nello stack per continuare a eseguire il codice che gestirà l'eccezione, ma non c'è più.

5

L'OutOfMemoryException è probabilmente lanciata perché si sta eseguendo un programma a 32 bit, con il grafico della memoria non è stata indicata la quantità di RAM del sistema, quindi forse provare a costruirla a 64 bit, e magari usare MemoryFailPoint per prevenire questo si verifica comunque.

Si potrebbe anche farci sapere cosa c'è nella funzione OutOfMemory() per un'immagine più chiara.

P.S. StackOverFlow è l'unico errore che non può essere gestito.

Modifica: come già accennato, e ho pensato che fosse solo logico e quindi non ne ho parlato prima, se per esempio si tenta di allocare più memoria di quella che si è "risparmiata", allora non è possibile farlo e un si verifica un'eccezione. Dato che stai allocando array di grandi dimensioni con i tuoi dati. Add() cade prima che si verifichi l'aggiunta "illegale" finale, quindi c'è ancora memoria libera.

Quindi presumo che sia a questo punto data.Add (buffer); il problema si verifica durante la creazione dell'array quando si supera il limite del processo di 2 GB aggiungendo un array di byte da 400 MB a "dati", ad es. una serie di circa 1 miliardo di oggetti a 4 byte, un pezzo che mi aspetterei di avere intorno ai 400MB.

P.S. Fino a. 4.5 L'allocazione massima della memoria di processo è 2 GB, dopo che sono disponibili 4,5 più grandi.

+0

Ah, così non ha memoria, il che mi porta a credere che tu stia colpendo i limiti delle applicazioni e non necessariamente i limiti del sistema. –

+0

Non sono interessato a come risolverlo, non c'è alcun problema di attivazione, si tratta di ricerca e comprensione di come funziona CLR – GameScripting

+0

P.S. StackOverflow non è l'unico che non puoi catturare. Hai anche 'AccessViolationException'. –

0

Non è sicuro se questo risponde alla tua domanda, ma una (semplificato) spiegazione su come si decide quali oggetti per ripulire è questa:

Il garbage collector prende ogni filo conduttore nel programma e tutti i segni di livello superiore oggetti, ovvero tutti gli oggetti accessibili dai frame dello stack (ovvero, tutti gli oggetti puntati da variabili locali nei punti in cui si trova attualmente l'esecuzione) e tutti gli oggetti puntati dai campi statici.

Quindi, contrassegna gli oggetti di livello successivo, ovvero tutti gli oggetti puntati da tutti i campi degli oggetti precedentemente contrassegnati. Questo passaggio viene ripetuto fino a quando non vengono contrassegnati nuovi oggetti.

Poiché C# non consente puntatori nel contesto normale, una volta completato il passaggio precedente, è garantito che gli oggetti non contrassegnati non sono accessibili dal codice successivo e pertanto possono essere ripuliti in modo sicuro.

Nel tuo caso, se gli oggetti che hai assegnato per aggiungere pressione al gestore di memoria non vengono mantenuti per riferimento, significa che il GC avrà la possibilità di pulirli. Inoltre, tieni presente che OutOfMemoryException fa riferimento alla memoria gestita del tuo programma CLR, mentre il GC funziona un po 'al di fuori di quella "scatola".

0

Il motivo per cui è possibile intercettare un'eccezione OutOfMemoryException è perché il designer del linguaggio ha deciso di lasciarlo. Il motivo per cui questo è a volte (ma non di solito) pratico è perché in alcuni casi è una situazione recuperabile.

Se si tenta di allocare un array enorme, è possibile ottenere un OutOfMemoryException, ma la memoria per quell'array enorme in realtà non è stata allocata, quindi l'altro codice sarà ancora in grado di funzionare senza problemi. Inoltre, lo srotolamento dello stack dovuto all'eccezione potrebbe causare l'ottenimento di altri oggetti per la garbage collection, aumentando ulteriormente la quantità di memoria disponibile.

0

Il metodo OutOfMemory() crea la struttura dati (List<byte[]>) locale per l'ambito del metodo. Mentre il tuo thread di esecuzione è all'interno del metodo OutOfMemory, il frame dello stack corrente è considerato la radice GC per l'Elenco. Una volta che il thread finisce nel blocco catch, il frame dello stack è stato scoppiato e l'elenco è effettivamente diventato irraggiungibile. Pertanto il garbage collector determina che è in grado di raccogliere l'elenco in tutta sicurezza (cosa che fa come hai osservato nel grafico della memoria).

Problemi correlati