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.
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
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. –
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