2010-09-29 20 views
5

Nella nostra applicazione, stiamo leggendo un file XPS utilizzando la classe System.IO.Packaging.Package. Quando leggiamo da uno stream di un PackagePart, possiamo vedere dal Task Manager che il consumo di memoria dell'applicazione aumenta. Tuttavia, al termine della lettura, il consumo di memoria non torna a quello che era prima della lettura dal flusso.La lettura dal flusso di PackagePart non rilascia memoria

Per illustrare il problema, ho scritto un semplice esempio di codice che è possibile utilizzare in un'applicazione wpf autonoma.

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();  
     } 
} 

Il metodo ReadPackage() legge tutto il contenuto del flusso degli oggetti PackagePart in un array locale. Nell'esempio, ho usato un documento XPS di 1000 pagine come sorgente del pacchetto per poter vedere facilmente la modifica del consumo di memoria dell'applicazione. Sulla mia macchina, il consumo di memoria della app autonoma inizia a 18 MB, quindi sale a 100 MB dopo aver chiamato il metodo. Chiamando di nuovo il metodo è possibile aumentare nuovamente il consumo di memoria, ma può ridursi a 100 MB. Tuttavia, non ricade più a 18 MB.

Qualcuno ha riscontrato questo problema durante l'utilizzo di PackagePart? O lo sto usando male? Penso che l'implementazione interna di PackagePart stia memorizzando nella cache i dati letti.

Grazie!

+0

Non ho idea del motivo per cui questa domanda è stata downvoted. –

risposta

0

Non si specifica come si misura il "consumo di memoria" della propria applicazione, ma forse si sta utilizzando il task manager? Per avere una visione migliore di ciò che sta accadendo suggerisco di esaminare alcuni contatori delle prestazioni per la tua applicazione. Sono disponibili entrambi i contatori delle prestazioni della memoria di processo e di heap .NET.

Se si desidera veramente comprendere i dettagli di come la propria applicazione utilizza la memoria, è possibile utilizzare Microsoft CLR profiler.

Ciò che vedete potrebbe essere il risultato dell'espansione dello heap .NET per ospitare un file molto grande. I grandi oggetti sono posizionati sul Large Object Heap (LOH) e, anche se la memoria .NET è raccolta inutilmente, la memoria libera non viene mai restituita al sistema operativo. Inoltre, gli oggetti sul LOH non vengono mai spostati durante la garbage collection e questo può frammentare il LOH che esaurisce lo spazio di indirizzi disponibile anche se c'è molta memoria libera.

Qualcuno ha riscontrato questo problema durante l'utilizzo di PackagePart? O lo sto usando male?

Se si desidera controllare le risorse utilizzate dal pacchetto non lo si utilizza nel modo migliore. I pacchetti sono monouso e, in generale, si dovrebbe usare in questo modo:

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

Al termine delle risorse using dichiarazione consumate dal pacchetto sono o già rilasciato o possono essere garbage collection.

Se si desidera veramente mantenere il membro _package del modulo, è necessario chiamare a un certo punto Close() (o IDisposable.Dispose()) per rilasciare le risorse. Chiamare il numero GC.Collect() non è raccomandato e non sarà necessariamente in grado di riciclare le risorse utilizzate dal pacchetto. Qualsiasi memoria gestita (ad es. Buffer di pacchetti) raggiungibile da _package non sarà raccolta di dati inutili, a prescindere dalla frequenza con cui si tenta di forzare una garbage collection.

+0

grazie per la risposta! Stavo usando TaskManager. yup cercherà il profiler CLR come un'altra opzione.quello di cui sono preoccupato è perdere tempo a cercare una soluzione che sia in realtà un bug nel codice di implementazione PackagePart interno che solo Microsoft può risolvere. Ho anche provato a sostituire lo stream di PackagePart con un FileStream che legge il contenuto di un file da 1 MB nell'array. Questo viene fatto lo stesso numero di volte del codice sopra. È fondamentalmente la stessa procedura ma solo la lettura da un flusso diverso. In questo caso, la memoria viene raccolta. Non ha nemmeno raggiunto 50 MB. – bjutus

+0

hmm. ho provato a chiamare GC.Collect() subito dopo aver chiamato ReadPackage() ma non è successo nulla. tuttavia, ho chiamato _package.Close() poi GC.Collect() dopo ReadPackage() e l'utilizzo della memoria è sceso a circa 20 MB da 100 MB. quindi forse il pacchetto conteneva i riferimenti dei flussi? – bjutus

+0

ha visto di nuovo i tuoi aggiornamenti nel tuo answer.thanks. Ho solo provato GC.Collect() per controllare se la memoria non è davvero aiutata da nessuno. sembra come se fosse fino a quando il pacchetto è chiuso. lo chiamiamo vicino al pacchetto quando non ne abbiamo più bisogno. il problema è che è la principale fonte di dati nella nostra app, quindi deve rimanere aperta durante tutta la vita dell'app. lo stream di PackagePart è qualcosa di cui non abbiamo bisogno in tutto. per questo motivo ci aspettiamo che la memoria venga liberata man mano che abbandoniamo l'ambito del metodo ReadPackage(). forse il pacchetto ha un qualche tipo di cache interna ... – bjutus