2010-09-29 17 views
5

W naszej aplikacji odczytujemy plik XPS przy użyciu klasy System.IO.Packaging.Package. Kiedy czytamy ze strumienia PackagePart, widzimy w Menedżerze zadań, że wzrasta zużycie pamięci aplikacji. Jednak po zakończeniu odczytu zużycie pamięci nie powróci do wartości sprzed odczytu ze strumienia.Odczytywanie ze strumienia PackagePart nie zwalnia pamięci

Aby zilustrować problem, napisałem prosty przykład kodu, którego można użyć w samodzielnej aplikacji wpf.

public partial class Window1 : Window 
{ 
     public Window1() 
     { 
      InitializeComponent(); 

      _package = Package.Open(@"c:\test\1000pages.xps", FileMode.Open, FileAccess.ReadWrite, FileShare.None); 

     } 

     private void ReadPackage() 
     { 
      foreach (PackagePart part in _package.GetParts()) 
      { 
       using (Stream partStream = part.GetStream()) 
       { 
        byte[] arr = new byte[partStream.Length]; 
        partStream.Read(arr, 0, (int)partStream.Length); 
        partStream.Close(); 
       } 
      } 
     } 

     Package _package; 
     private void Button_Click(object sender, RoutedEventArgs e) 
     { 
      ReadPackage();  
     } 
} 

Metoda ReadPackage() odczyta zawartość strumienia obiektów PackagePart do lokalnej tablicy. W próbce użyłem dokumentu XPS o wielkości 1000 stron jako źródła pakietu, aby łatwo zobaczyć zmianę zużycia pamięci aplikacji. Na moim komputerze zużycie pamięci samodzielnej aplikacji zaczyna się od 18 MB, a następnie wzrasta do 100 MB po wywołaniu metody. Ponowne wywołanie metody może ponownie zwiększyć zużycie pamięci, ale może wrócić do 100 MB. Jednak już nie wraca do 18 MB.

Czy ktoś doświadczył tego podczas korzystania z PackagePart? Czy używam go źle? Myślę, że wewnętrzna implementacja PackagePart buforuje odczytane dane.

Dziękujemy!

+0

Nie mam pojęcia, dlaczego to pytanie zostało odrzucone. –

Odpowiedz

0

Nie określasz sposobu mierzenia "zużycia pamięci" w swojej aplikacji, ale może używasz menedżera zadań? Aby uzyskać lepszy obraz tego, co się dzieje, sugerujemy zbadanie niektórych liczników wydajności dla twojej aplikacji. Dostępne są zarówno sterty .NET, jak i ogólne liczniki wydajności pamięci procesowej.

Jeśli naprawdę chcesz zrozumieć, w jaki sposób twoja aplikacja korzysta z pamięci, możesz użyć Microsoft CLR profiler.

To, co widzisz, może być wynikiem powiększania się stosu .NET, aby pomieścić bardzo duży plik. Duże obiekty są umieszczane na stertach dużych obiektów (LOH), a nawet jeśli pamięć .NET jest zbędna, wolna pamięć nigdy nie jest zwracana do systemu operacyjnego. Ponadto obiekty na LOH nigdy nie są przenoszone podczas usuwania śmieci, co może powodować fragmentację LOH, wyczerpując dostępną przestrzeń adresową, mimo że jest dużo wolnej pamięci.

Czy ktoś tego doświadczył podczas korzystania z PackagePart? Czy używam go źle?

Jeśli chcesz kontrolować zasoby używane przez pakiet nie używasz go w jak najlepszy sposób. Paczki są jednorazowego użytku i w ogóle powinna go używać tak:

using (var package = Package.Open(@"c:\test\1000pages.xps", FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { 
    // ... process the package 
} 

Pod koniec using rachunku zasobów zużywanych przez pakiet albo są już zwolnione lub mogą być zbierane śmieci.

Jeśli naprawdę chcesz zachować element członkowski formularza _package, powinieneś w pewnym momencie zadzwonić pod numer Close() (lub IDisposable.Dispose()), aby zwolnić zasoby. Dzwonienie pod numer GC.Collect() nie jest zalecane i nie będzie w stanie odtworzyć zasobów używanych przez pakiet. Jakakolwiek pamięć zarządzana (np. Bufory pakietów), które można uzyskać od _package, nie będzie zbierana podczas czyszczenia pamięci, bez względu na to, jak często będziesz wymuszał zbieranie śmieci.

+0

dzięki za odpowiedź! używałem TaskManager. yup spróbuje profilera CLR jako innej opcji.martwię się, że marnuje czas na znalezienie rozwiązania, które w rzeczywistości jest błędem wewnętrznego kodu implementacji PackagePart, który może naprawić tylko Microsoft. Próbowałem też zastąpić strumień PackagePart strumieniem FileStream, który odczytuje zawartość pliku 1MB do tablicy. Robi się to tyle razy, ile wynosi powyższy kod. Jest to zasadniczo ta sama procedura, ale tylko odczyt z innego strumienia. W tym przypadku pamięć jest gromadzona. Nie osiągnął nawet 50 MB. – bjutus

+0

hmm. Próbowałem wywoływać GC.Collect() zaraz po wywołaniu ReadPackage(), ale nic się nie stało. jednak nazwałem _package.Close(), a następnie GC.Collect() po ReadPackage() i użycie pamięci spadło z powrotem do około 20 MB z 100 MB. więc być może pakiet trzymał referencje strumieni? – bjutus

+0

zobaczyłem twoje aktualizacje w twojej answer.thanks. Próbowałem tylko GC.Collect(), aby sprawdzić, czy pamięć naprawdę nie jest wspomagana przez nikogo. wygląda na to, dopóki pakiet nie zostanie zamknięty. tak, możemy zadzwonić blisko na paczkę, gdy już jej nie potrzebujemy. Problem polega na tym, że jest to główne źródło danych w naszej aplikacji, więc musi pozostać otwarte przez całe życie aplikacji. strumień PackagePart jest czymś, czego nie potrzebujemy w całym tekście. dlatego oczekujemy, że pamięć zostanie uwolniona, gdy opuszczymy zakres metody ReadPackage(). może pakiet ma jakieś wewnętrzne buforowanie ... – bjutus