2013-02-05 25 views
19

sto usando il codice qui sotto per creare un riquadro live, basato su un elemento dell'interfaccia utente. Restituisce il su un WriteableBitmap, salva la bitmap + restituisce il nome file. Questo metodo viene eseguito in un task agent in background di Windows Phone e sto eseguendo dei limiti di memoria.WriteableBitmap Perdita di memoria?

private string CreateLiveTileImage(Canvas uiElement, int width, int heigth) 
{ 
    var wbmp = new WriteableBitmap(width, heigth); 
    try 
    { 
     wbmp.Render(uiElement, null); 
     wbmp.Invalidate(); 

     var tileImageName = _liveTileStoreLocation; 
     using (var stream = new IsolatedStorageFileStream(tileImageName, FileMode.Create, FileAccess.Write, IsolatedStorageFile.GetUserStoreForApplication())) 
     { 
     wbmp.SaveJpeg(stream, width, heigth, 0, 100); 
     stream.Close(); 
     } 

     uiElement = null; 
     wbmp = null; 
     GC.Collect(); 
     return "isostore:" + tileImageName; 
    } 
    catch (Exception exception) 
    { 
     // ... 
    } 
    return null; 
} 

ho fatto alcuni test e il problema è: Questo metodo perdite di memoria, ma non so il motivo per cui /dove ?!

ho fatto anche alcune piste di prova - prima del primo eseguito in questo modo:

Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage 
7.249.920 Bytes 

Questo è ok, dal momento che il debugger è collegato, che utilizza circa 2 MB di memoria.

Facendo alcuni più esecuzioni di questo metodo (arretrato a correre di nuovo metodo di debugger):

Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage 
8851456 long + 40960 
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage 
8892416 long + 245760 
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage 
9138176 long + 143360 
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage 
9281536 long + 151552 
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage 
9433088 long + 143360 
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage 
9576448 long + 139264 
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage 
9715712 long + 139264 
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage 
9859072 long + 143360 
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage 
10006528 long + 147456 

Così la memoria, utilizzata da questo metodo aumenta.

Ma perché? Secondo me non ci sono riferimenti che impediscono il recupero degli oggetti.

UPDATE 04.05.2013

Ciao,

grazie per tutte le vostre risposte! Come suggerito, ho ridotto il codice + finnally è stato in grado di riprodurre il problema in poche righe di codice.

void Main() 
{ 
    for (int i = 0; i < 100; i++) 
    { 
     CreateImage(); 
    } 
} 

private void CreateImage() 
{ 
    var rectangle = CreateRectangle(); 
    var writeableBitmap = new WriteableBitmap(rectangle, rectangle.RenderTransform); 

    rectangle = null; 
    writeableBitmap = null; 
    GC.Collect(); 
} 

private Rectangle CreateRectangle() 
{ 
    var solidColorBrush = new SolidColorBrush(Colors.Blue); 
    var rectangle = new Rectangle 
    { 
    Width = 1000, 
    Height = 1000, 
    Fill = solidColorBrush // !!! THIS causes that the image writeableBitmap never gets garbage collected 
    }; 

    return rectangle; 
} 

Dopo aver avviato App: ApplicationCurrentMemoryUsage: "11 681 792 Bytes"

1 Iterazione - ApplicationCurrentMemoryUsage: "28 090 368 Bytes"

5 iterazioni - ApplicationCurrentMemoryUsage: "77 111 296 Byte"

20 iterazioni - ApplicationCurrentMemoryUsage: "260 378 624 Bytes"

Dopo 23 iterazioni: Eccezione memoria esaurita. Ln .: var writeableBitmap = new WriteableBitmap (rettangolo, rettangolo.RenderTransform);

Solo commentando la riga "Fill = solidColorBrush", il metodo CreateImage() è stato chiamato 100 volte senza problemi - dopo la 100a iterazione, l'utilizzo della memoria era di circa "16 064 512 byte".

Quindi sembra che il problema sia il pennello !! Quando viene utilizzato per riempire un elemento dell'interfaccia utente e in seguito questo elemento di interfaccia utente viene visualizzato su una bitmap scrivibile, la bitmap non viene mai raccolta.

Ovviamente questo non ha senso secondo me. Il pennello esegue run-of-scope in modo che sia semplicemente necessario raccogliere dati inutili! (L'impostazione del pennello su null dopo l'utilizzo non ha modificato nulla)

Molti dei miei elementi dell'interfaccia utente utilizzano un pennello per il riempimento, quindi non posso semplicemente rimuovere l'utilizzo dei pennelli. Cosa ne pensi di questo problema?

+1

Si noti che di solito non c'è alcun motivo per chiamare direttamente GC.Collect(). Può essere dannoso, bloccare la tua applicazione e ha un impatto sulle prestazioni. –

+2

È vero, era più una sorta di "tentativo disperato", in realtà non fa nulla e non ha alcuna influenza sul consumo di memoria. – Hannes

+0

Sei curioso di sapere cosa succede se finalizzi la tela? – Tebc

risposta

10

Il problema è che rectangle.RenderTransform è un'istanza di un oggetto e se si imposta writableBitmap su null il rettangolo.RenderTransform Object è ancora vivo e contiene il rettangolo nella memoria ... quindi la soluzione è modificare il codice come segue:

private void CreateImage() 
{ 
    var rectangle = CreateRectangle(); 
    var writeableBitmap = new WriteableBitmap(rectangle, rectangle.RenderTransform); 

    rectangle.RenderTransform = null; //and your memory would be happy ;) 
    rectangle = null; 
    writeableBitmap = null; 
    GC.Collect(); 
} 

vedere gli screenshot di memoria ...

prima:

memory without setting rectangle.RenderTransform to null

dopo:

memory with setting rectangle.RenderTransform to null

+0

Come si ottiene l'analisi della memoria? Sto usando VS 2013 Preview/Ultimate. –

+0

È possibile aprire questa prospettiva tramite: DEBUG> Avvia analisi delle prestazioni (Alt + F2) –

+0

Grazie, l'ho trovato. –

0

Alla fine nel tentativo di cattura dovrebbe essere wbmp = null per un calcio d'inizio.

E lo stai rendendo, il che significa che stai collegando quell'oggetto ad una lista esterna (la funzione). Pertanto nessuna quantità di GC.Collect la raccoglierà effettivamente poiché è ancora "in gioco".

Imposta la sorgente di unielement su null, che può eliminare il riferimento allegato perché il GC lo sta ignorando.

+0

grazie, ho provato entrambi, ma non ha aiutato - si prega di vedere la pubblicazione aggiornata - il problema esiste ancora. – Hannes

0

È possibile limitare le dimensioni dei scrivibile altezza x larghezza immagine bitmap a dimensioni più piccole, come quando aumenta la sua dimensione ci vuole più memoria in modo da poter limitare la sua dimensione iniziale in un primo momento quindi salvare in formato JPEG con il formato delle mattonelle originali

+0

Ma questo rallegra l'immagine, che porta a non quella buona immagine qulity – Hannes

-1

Prova a mettere questa parte:

 uiElement = null; 
     wbmp = null; 
     GC.Collect(); 

a finalmente clausola;

+0

Ho provato questo ma non risolve il problema, si prega di vedere la pubblicazione aggiornata. – Hannes

+0

-1 l'impostazione delle cose su 'null' è solo una programmazione carico-setta. Non fa nulla di utile e in alcuni casi può effettivamente interferire con la garbage collection. Inoltre, le chiamate a 'GC.Collect()' nel codice di produzione sono una gigantesca bandiera rossa che un programmatore non sapeva cosa stavano facendo e sta lavorando contro il garbage collector piuttosto che con esso. –

+0

@CodyGray Sto solo dicendo che se hai letto correttamente la domanda originale, non ti suggerisco di scrivere null e GC.Collect(), è parte del codice originale, ho solo suggerito di spostarlo per bloccare definitivamente. Di più, la risposta accettata ha lo stesso valore nullo e GC.Collect(), non è sicuro, perché mi hai meno specificatamente e cosa vuoi da me. –

0

ottengo stessa eccezione quando convertito UIElement a WriteableBitmap in TaskAgent. Ho appena provato tante volte. Ho trovato una soluzione che può essere lavorata.

wbmp.SaveJpeg(stream, width, heigth, 0, 60); 

è possibile modificare la qualità a 60 quando si salva l'unità in streaming. Riduce bene l'uso della memoria. Puoi provarlo.