2009-11-05 6 views
12

System: Windows XP SP3, .NET 3.5, 4 GB RAM, Dual 1.6 HzW jaki sposób upewniasz się, że WPF udostępnia duży obszar BitmapSource z pamięci?

Mam aplikację WPF, która ładuje i przenosi (za pomocą animacji Storyboard) bardzo duże pliki PNG. Te pliki PNG mają rozdzielczość 8190 x 1080. Po uruchomieniu aplikacji wydaje się, że buforuje obrazy, a pamięć System powoli się skacze. W końcu dławi system i generuje wyjątek OutOfMemoryException.

Oto kroki Jestem obecnie starać się rozwiązać ten problem:

1) Jestem usuwając obiekty BitmapSource z aplikacji

2) Jestem ustalające BitmapSource BitmapCacheOption None gdy załadować BitmapSource

3) Zamrażam BitmapSource po załadowaniu.

4) Usuwam wszystkie odniesienia do obrazu, który korzysta ze źródła, jak również wszelkie odniesienia do samego źródła.

5) Ręczne wywołanie GC.Collect() po wykonaniu powyższych kroków zostało zakończone.

Mając nadzieję, że zrozumiemy, dlaczego WPF wisi na pamięci dla tych obrazów i możliwe rozwiązanie, aby upewnić się, że pamięć użyta do ich załadowania jest prawidłowo odzyskana.

Odpowiedz

21

Z pewnością dużo włożyłeś w to. Myślę, że głównym problemem jest to, że BitmapCacheOption.None nie uniemożliwia buforowania bazowego BitmapDecoder (ów).

Istnieje kilka trudnych do rozwiązania, takie jak to robi GC.Collect(), załadunek 300 małych obrazów z 300 różnych URI, i nazywając GC.Collect() ponownie, ale prosta jest prosta:

zamiast załadunkiem od URI, wystarczy skonstruować Stream i przekazać go do konstruktora BitmapFrame za:

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

powodem tego jest to, że powinien działać ładowanie ze strumienia całkowicie wyłącza pamięć podręczną. Nie tylko źródło najwyższego poziomu nie jest buforowane, ale żaden z wewnętrznych dekoderów nie jest buforowany.

Dlaczego BitmapCacheOption.OnLoad? Wygląda to na sprzeczne z intuicją, ale ta flaga ma dwa efekty: Umożliwia buforowanie, jeśli buforowanie jest możliwe, i powoduje obciążenie w EndInit(). W naszym przypadku buforowanie jest niemożliwe, więc wszystko, co robi, powoduje natychmiastowe obciążenie.

Oczywiście będziesz chciał uruchomić ten kod ze swojego wątku UI, a następnie zamrozić BitmapSource, abyś mógł go przenieść.

Możesz także zastanawiać się, dlaczego nie użyłem BitmapCreateOptions.IgnoreImageCache. Poza faktem, że buforowanie jest niemożliwe przy braku podanego identyfikatora URI, IgnoreImageCache nie całkowicie ignoruje pamięć podręczną obrazu: ignoruje ją tylko do odczytu. Więc nawet jeśli ustawiono IgnoreImageCache, załadowany obraz jest nadal wstawiany do pamięci podręcznej. Różnica polega na tym, że istniejący obraz w pamięci podręcznej jest ignorowany.

+0

Źródło BitmapSource = new BitmapSource() nie będzie się kompilować i nie jestem pewien dlaczego. Zgłasza ten błąd: Błąd Nie można utworzyć wystąpienia klasy abstrakcyjnej lub interfejsu "System.Windows.Media.Imaging.BitmapSource" – discorax

+0

Ahh..it kompiluje się, gdy używam BitmapImage zamiast BitmapSource. Teraz, w jaki sposób spowoduje to problemy? :) – discorax

+0

Takie podejście wygląda obiecująco. Będę kontynuować test. – discorax

Powiązane problemy