domenica 17 ottobre 2010

GDI – Leak – WinCE – C#

Suona strano avere dei memory leak in codice gestito ma sono molto più frequenti di quello che si possa pensare. Alcune settimane fà ho tenuto una sessione di code review presso un mio cliente e ho speso alcuni giorni a sistemare diversi memory leak su una programma scritto in C# per Windows CE.

Quello che bisogna tenere a mente che tutti gli oggetti GDI (Bitmap, Font, Brush, Pen, ecc.) di C# in realtà sono dei wrapper sopra oggetti Win32. Tali oggetti sono referenziati tramite Handle e devono essere rilasciati. Il GC non lo fà per voi in modo automatico perchè semplicemente non li può gestire. Sopratutto nelle applicazioni mobile, in cui le risorse sono limitate, bisogna porre particolare attenzione a questo.

Durante la sessione di debug ho utilizzato questo tool (GDIView) che mi ha aiutato a verificare quali handle non venivano rilasciati.

Alcuni consigli pratici:

  • Richiamare sempre il Dispose di tutti gli oggetti grafici utilizzati, meglio ancora usare sempre la keword “using” per richiamare sempre in modo implicito il Dispose, esempio:
   1: SolidBrush fillBrush = new SolidBrush(Color.White);
   2: graphImage.FillRectangle(fillBrush, 0, 0, UI_CLIENT_WIDTH, UI_HEIGHT);
   3: fillBrush.Dispose();
   4:  
   5: // or
   6:  
   7: using(SolidBrush fillBrush = new SolidBrush(Color.White))
   8: {
   9:     graphImage.FillRectangle(fillBrush, 0, 0, UI_CLIENT_WIDTH, UI_HEIGHT);
  10: }
  11:  

 



  • Quando si riassegna una Bitmap assicurarsi che l’oggetto precedente sia deallocato, esempio:


   1: // wrong
   2: picCapture.Image = new Bitmap(32, 32);
   3:  
   4: // good
   5: if(picCapture.Image != null)
   6:     picCapture.Image.Dispose();
   7: picCapture.Image = new Bitmap(32, 32);

 



  • Quando si utilizza una Image contenuta in una ImageList è bene ricordare quanto esposto su MSDN: “The returned bitmap is a copy of the original image and should be disposed of using the Image.Dispose method.”. In altre parole tutte le get da una ImageList clonano una nuova immagine che deve essere rilasciata tramite un Dispose!
  • Quando si utilizzano direttamente le funzioni native Win32 porre attenzione se queste ritornano una nuova istanza di una risorsa in memoria. Leggere attentamente la loro documentazione!
  • Ogni volta che si alloca memoria tramite funzioni native Win32 è vostra responsabilità deallocarla.
  • Quando si crea un nuovo oggetto non gestito, salvare il riferimento (handle) in una variabile non accessibile dall’esterno. Se per qualsiasi motivo il riferimento viene sovrascritto non sarà più possibile deallocare correttamente la risorsa non gestita.