Come ci si assicura che WPF rilasci grandi BitmapSource dalla memoria?

Sistema: Windows XP SP3, .NET 3.5, 4 GB RAM, Dual 1.6gHz

Ho un’applicazione WPF che carica e transizioni (usando animazioni di Storyboard) PNG estremamente grandi. Questi PNG hanno una risoluzione di 8190×1080. Mentre l’applicazione viene eseguita, sembra che le immagini vengano memorizzate nella cache e la memoria del sistema si avvicini lentamente. Alla fine soffoca il sistema e lancia l’OutOfMemoryException.

Ecco i passi che sto prendendo per cercare di risolvere questo problema:

1) Sto rimuovendo gli oggetti BitmapSource dall’app

2) Sto impostando BitmapCourceOption su BitmapSource su None quando carico BitmapSource

3) Sto congelando BitmapSource una volta caricato.

4) Sto cancellando tutti i riferimenti all’immagine che usa la fonte così come ogni riferimento alla fonte stessa.

5) Chiamare manualmente GC.Collect () dopo aver completato i passaggi precedenti.

Sperando di capire perché WPF è appeso alla memoria per queste immagini e una ansible soluzione per garantire che la memoria utilizzata per caricarle sia correttamente recuperata.

Sicuramente hai lavorato molto su questo. Penso che il problema principale sia che BitmapCacheOption.None non impedisce la cache dei BitmapDecoder sottostanti.

Ci sono diverse soluzioni complicate come fare un GC.Collect (), caricare 300 piccole immagini da 300 diversi Uris e chiamare nuovamente GC.Collect (), ma quello semplice è semplice:

Invece di caricare da un Uri, basta build un stream e passarlo al costruttore di BitmapFrame:

var source = new BitmapImage(); using(Stream stream = ...) { source.BeginInit(); source.StreamSource = stream; source.CacheOption = BitmapCacheOption.OnLoad; // not a mistake - see below source.EndInit(); } 

Il motivo per cui questo dovrebbe funzionare è che il caricamento da uno stream disabilita completamente la cache. Non solo la sorgente di primo livello non viene memorizzata nella cache, ma nessuno dei decodificatori interni viene memorizzato nella cache.

Perché BitmapCacheOption.OnLoad? Sembra controintuitivo, ma questo flag ha due effetti: abilita il caching se è ansible eseguire il caching e fa sì che il caricamento avvenga su EndInit (). Nel nostro caso il caching è imansible, quindi tutto ciò fa sì che il caricamento avvenga immediatamente.

Ovviamente vorrai eseguire questo codice dal tuo thread dell’interfaccia utente, quindi congelare BitmapSource in modo da poterlo spostare.

Potresti anche chiedermi perché non ho usato BitmapCreateOptions.IgnoreImageCache. A parte il fatto che la memorizzazione nella cache è imansible con nessun URI dato, IgnoreImageCache non ignora completamente la cache dell’immagine: la ignora solo per la lettura. Quindi, anche se IgnoreImageCache è impostato, l’immagine caricata viene ancora inserita nella cache. La differenza è che l’immagine esistente nella cache viene ignorata.