2010-11-11 14 views
16

Attualmente eseguo il debug di un metodo che utilizziamo per contrassegnare le immagini con un determinato testo prima di visualizzarle nel nostro sistema.Prevenire OutOfMemoryException con GC.AddMemoryPressure()?

Il metodo di tag si presenta così in questo momento:

private static Image TagAsProductImage(Image image) 
{ 
    try 
    { 
     // Prepares the garbage collector for added memory pressure (500000 bytes is roughly 485 kilobytes). 
     // Should solve some OutOfMemoryExceptions. 
     GC.AddMemoryPressure(500000); 

     using (Graphics graphics = Graphics.FromImage(image)) 
     { 
      // Create font. 
      Font drawFont = new Font("Tahoma", image.Width*IMAGE_TAG_SIZE_FACTOR); 

      // Create brush. 
      SolidBrush drawBrush = new SolidBrush(Color.Black); 

      // Create rectangle for drawing. 
      RectangleF drawRect = new RectangleF(0, image.Height - drawFont.GetHeight(), image.Width, 
                drawFont.GetHeight()); 

      // Set format of string to be right-aligned. 
      StringFormat drawFormat = new StringFormat(); 
      drawFormat.Alignment = StringAlignment.Far; 

      // Draw string to screen. 
      graphics.DrawString(TAG_TEXT, drawFont, drawBrush, drawRect, drawFormat); 
     } 
    } 
    // If an out of memory exception is thrown, return the unaltered image. 
    catch(OutOfMemoryException) 
    { 
     GC.RemoveMemoryPressure(500000); 
     return image; 
    } 

    GC.RemoveMemoryPressure(500000); 
    return image; 
} 

Per mettere le cose nel contesto: Questo metodo viene chiamato dopo che un'immagine è stata recuperata dal nostro server di immagine e salvata in una cache locale (che il nostro sistema condivide con altri sistemi che richiedono le stesse immagini).

Abbiamo riscontrato problemi con OutOfMemoryExceptions quando si raggiunge using (Graphics... (quando l'immagine deve essere recuperata dal server prima della codifica, se l'immagine è presente nella cache la codifica non è stato un problema).

Per impedire/eludere l'OutOfMemoryException ho provato tre approcci diversi e mentre funzionano non mi piacciono affatto nessuno di loro.

Prima ho provato a fare un generico GC.Collect(); prima di chiamare Graphics.FromImage(image) che ha funzionato (ovviamente) ma non mi piace forzare Collects poiché lascia un grande successo sulle prestazioni.

Il mio secondo approccio era chiamare GC.Collect() nella dichiarazione catch e quindi chiamare ricorsivamente TagAsProductImage(image) ma questo potrebbe portare a un ciclo infinito se GC non riesce a liberare memoria sufficiente.

E alla fine ho finito con il codice di cui sopra, che non posso dire che mi piaccia di entrambi.

Probabilmente posso scappare usando GC.Collect() poiché l'intera operazione di ottenere l'immagine dal servizio -> salvataggio -> tagging è abbastanza grande quindi il rendimento della raccolta sarà minimo, ma mi piacerebbe molto una soluzione migliore.

Se qualcuno ha una soluzione intelligente a questo, si prega di condividere.

risposta

16

Se si sta cercando un modo per assicurarsi di disporre di memoria sufficiente per un'operazione, utilizzare MemoryFailPoint.

Con questo, attraverso un using, è possibile definire una regione in cui è necessaria una certa quantità di memoria. Se ciò non è disponibile, verrà generato un valore recuperabile InsufficientMemoryException.

Vedere http://msdn.microsoft.com/en-us/library/system.runtime.memoryfailpoint.aspx per ulteriori informazioni.

+0

Non sapevo nulla del MemoryFailPoint, quindi grazie per l'heads up va direttamente al mio angolo "Codice utile da ricordare". Purtroppo non è stato d'aiuto, ho provato ad allocare tutto da 1 a 200 MB, è riuscito in tutti i casi, ma ho comunque gettato un'eccezione OutOfMemoryException durante il tentativo di creare l'oggetto Graphics. Dovrà continuare a cercare (GDI + e la gestione della memoria può essere a volte un dolore). – Mantisen

+0

Lo prendo, fallisce, non sempre fallisce ??? Quello che dovresti fare prima di usare, fare un 'GC.GetTotalMemory (false)' e appena prima della fine del blocco using, un altro. La differenza dovrebbe essere la quantità effettiva di memoria utilizzata per quel processo. Se fornire quell'ammontare a 'MemoryFailPoint' non funziona allora, beh, sono fuori di idee: P. –

+0

Hai perfettamente ragione, fallisce solo quando l'immagine non era precedentemente nella cache, ma è stata recuperata dal nostro server di immagini proprio prima che questo metodo venga chiamato. La cosa strana è che 'MemoryFailPoint' riesce ad allocare memoria (ogni volta!) Ma' utilizzando (Graphics ... 'lancia comunque OutOfMemoryException se l'immagine non era precedentemente nella cartella cache. – Mantisen

10

Si sta avendo un problema diverso qui, questo codice utilizza molto poca memoria. Tristemente le eccezioni GDI + sono piuttosto scadenti. Diagnostica questo con TaskMgr.exe, scheda Processi. Visualizza + Seleziona colonne e spunta oggetti GDI, maniglie e oggetti USER.

Se il mio sospetto è corretto, vedrete il contatore Oggetti GDI per il vostro processo che sale costantemente mentre questo codice viene eseguito. Quando raggiunge 10.000 Windows decide che c'è qualcosa di fondamentalmente sbagliato nel codice e si rifiuta di creare altri handle. GDI + diventa un po 'goffy e segnala un errore di memoria insufficiente. Sbagliato, avrebbe dovuto essere un errore 'could not create handle'. Un codice di errore che non ha. .NET è impotente a migliorare l'eccezione.

Anyhoo, il motivo è che non si chiama Dispose() sul font e sul pennello.Avvolgili con lo usando la dichiarazione. Questo normalmente non causa problemi, ma il tuo programma utilizza apparentemente poca memoria raccolta rifiuti per avviare il thread del finalizzatore.

+0

Il codice che ho incluso è solo un piccolo metodo in una classe altrimenti abbastanza grande (vedi la descrizione di in quale contesto viene chiamato il metodo) e quella classe è a sua volta parte di un sistema molto pesante con molti utenti. Sono riuscito a rintracciare questo specifico errore in una OutOfMemoryException in questo particolare metodo (usando perfmon, taskmanager, ecc.), In più l'eccezione viene lanciata prima di usare il pennello o il font e dal momento che li ho in un uso per il Grafica che dovrebbero essere disposte quando l'oggetto Graphics è. Non dovrebbero? Ma sono d'accordo che le eccezioni GDI + sono scadenti. – Mantisen

+2

No, la grafica non dispone automaticamente nulla. –

+0

Neanche quando si inserisce un'istruzione 'using'? Ho sempre pensato che qualsiasi cosa inserita in un'istruzione using fosse eliminata quando si usciva dall'ambito di utilizzo. – Mantisen

Problemi correlati