2009-11-05 12 views
12

Sistema: Windows XP SP3, .NET 3.5, 4GB RAM, Dual 1.6gHz¿Cómo se asegura de que WPF libere BitmapSource de memoria?

Tengo una aplicación WPF que carga y transiciones (usando animaciones Storyboard) PNGs extremadamente grandes. Estos PNG tienen una resolución de 8190x1080. A medida que la aplicación se ejecuta, parece que almacena en caché las imágenes y la memoria del sistema avanza lentamente. Eventualmente, estrangula el sistema y lanza OutOfMemoryException.

Estos son los pasos que estoy tomando para tratar de resolver esto:

1) Estoy quitando los objetos BitmapSource desde la aplicación

2) Estoy configurando el BitmapSource BitmapCacheOption en Ninguno cuando cargo la BitmapSource

3) estoy congelando la BitmapSource una vez que se ha cargado.

4) Estoy suprimiendo todas las referencias a la imagen que utiliza la fuente, así como cualquier referencia a la propia fuente.

5) llamando manualmente GC.Collect() después de los pasos anteriores se han completado.

Con la esperanza de averiguar por qué WPF está colgando en la memoria de estas imágenes y una posible solución para garantizar que la memoria utilizada para cargarlos se recupera correctamente.

Respuesta

21

Ciertamente ha trabajado mucho en esto. Creo que el principal problema es que BitmapCacheOption.None no evita que los BitmapDecoder subyacentes se almacenen en caché.

Hay varias soluciones a este difíciles tales como hacer un GC.Collect(), la carga de 300 pequeñas imágenes de 300 diferentes Uris, y llamando GC.Collect() de nuevo, pero el sencillo es sencillo:

en lugar de cargar desde un Uri, simplemente construir una corriente y pasarlo al constructor de BitmapFrame:

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

la razón de que esto debería funcionar es que la carga de una corriente desactiva por completo la memoria caché. No solo la fuente de nivel superior no está en la memoria caché, sino que tampoco ninguno de los decodificadores internos está en la memoria caché.

¿Por qué BitmapCacheOption.OnLoad? Parece contradictorio, pero este indicador tiene dos efectos: habilita el almacenamiento en caché si el almacenamiento en caché es posible y causa que la carga suceda en EndInit(). En nuestro caso, el almacenamiento en caché es imposible, por lo que todo lo que hace causa que la carga suceda de inmediato.

Obviamente usted querrá ejecutar este código de descuento hilo de interfaz de usuario, y luego congelar la BitmapSource lo que puede pasar por encima.

También puede preguntarse por qué yo no uso BitmapCreateOptions.IgnoreImageCache. Aparte del hecho de que el almacenamiento en caché es imposible cualquiera sin URI dado, IgnoreImageCache no ignora por completo el caché de imagen: solo lo ignora para leer. Entonces, incluso si IgnoreImageCache está configurado, la imagen cargada aún se inserta en la caché. La diferencia es que la imagen existente en el caché se ignora.

+0

BitmapSource source = new BitmapSource() no se compilará y no estoy seguro de por qué. Lanza este error: Error No se puede crear una instancia de la clase o interfaz abstracta 'System.Windows.Media.Imaging.BitmapSource' – discorax

+0

Ahh .. se compila cuando uso BitmapImage en lugar de BitmapSource. Ahora, ¿cómo puede causar problemas? :) – discorax

+0

Este enfoque parece prometedor hasta ahora. Continuaré probando. – discorax

Cuestiones relacionadas