2010-09-29 12 views
5

En nuestra aplicación, estamos leyendo un archivo XPS utilizando la clase System.IO.Packaging.Package. Cuando leemos de un flujo de un paquete compartido, podemos ver en el Administrador de tareas que aumenta el consumo de memoria de la aplicación. Sin embargo, cuando se realiza la lectura, el consumo de memoria no vuelve a ser lo que era antes de leer desde la secuencia.La lectura desde la transmisión PackagePart no libera la memoria

Para ilustrar el problema, escribí un ejemplo de código simple que puede usar en una aplicación wpf independiente.

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

El método ReadPackage() leerá todos los contenidos de transmisión en los objetos PackagePart en una matriz local. En la muestra, utilicé un documento XPS de 1000 páginas como fuente del paquete para ver fácilmente el cambio en el consumo de memoria de la aplicación. En mi máquina, el consumo de memoria de la aplicación independiente comienza en 18 MB y luego aumenta a 100 MB después de llamar al método. Llamar nuevamente al método puede aumentar el consumo de memoria, pero puede volver a 100MB. Sin embargo, ya no recae en 18MB.

¿Alguien ha experimentado esto al usar PackagePart? ¿O lo estoy usando mal? Creo que la implementación interna de PackagePart está almacenando en caché los datos que se leyeron.

¡Gracias!

+0

No tengo idea de por qué esta pregunta fue downvoted. –

Respuesta

0

No especifica cómo se mide el "consumo de memoria" de su aplicación, pero ¿quizás esté utilizando el administrador de tareas? Para obtener una mejor visión de lo que está sucediendo, le sugiero que examine algunos contadores de rendimiento para su aplicación. Están disponibles los contadores de rendimiento de la memoria de proceso general y montón de .NET.

Si realmente quiere comprender los detalles de cómo su aplicación utiliza la memoria, puede usar el Microsoft CLR profiler.

Lo que ve puede ser el resultado del despliegue del montón .NET para acomodar un archivo muy grande. Los objetos grandes se colocan en el montón de objetos grandes (LOH) e incluso si la memoria .NET es basura, la memoria gratuita nunca se devuelve al sistema operativo. Además, los objetos en el LOH nunca se mueven durante la recolección de basura y esto puede fragmentar el LOH agotando el espacio de direcciones disponible a pesar de que hay mucha memoria libre.

¿Alguien ha experimentado esto al usar PackagePart? ¿O lo estoy usando mal?

Si desea controlar los recursos utilizados por el paquete, no lo está utilizando de la mejor manera. Los paquetes son desechables y, en general, se debe utilizar la siguiente manera:

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

Al final de los recursos de los estados using consumidos por el paquete o bien están ya en libertad o pueden ser basura recogida.

Si realmente desea conservar el miembro _package de su formulario, en algún momento debe llamar al Close() (o) para liberar los recursos. No se recomienda llamar al GC.Collect() y no será necesariamente capaz de reciclar los recursos utilizados por el paquete. Cualquier memoria administrada (por ejemplo, almacenamientos intermedios de paquetes) a la que se pueda acceder desde _package no será basura recolectada, sin importar la frecuencia con la que trate de forzar una recolección de basura.

+0

gracias por la respuesta! estaba usando TaskManager. yup probará CLR Profiler como otra opción.Lo que me preocupa es perder tiempo tratando de encontrar una solución que en realidad es un error en el código de implementación interno de PackagePart que solo Microsoft puede arreglar. También intenté reemplazar la transmisión PackagePart con un FileStream que lee el contenido de un archivo de 1MB en la matriz. Esto se hace la misma cantidad de veces que el código anterior. Básicamente es el mismo procedimiento, pero solo lee de un flujo diferente. En este caso, la memoria se está recopilando. Ni siquiera alcanzó los 50MB. – bjutus

+0

hmm. Intenté llamar a GC.Collect() inmediatamente después de llamar a ReadPackage() pero no pasó nada. sin embargo, llamé a _package.Close() luego a GC.Collect() después de ReadPackage() y el uso de la memoria volvió a bajar a unos 20MB desde 100MB. entonces ¿tal vez el paquete contenía las referencias de las transmisiones? – bjutus

+0

viste tus actualizaciones en tu respuesta. Gracias de nuevo. Solo intenté GC.Collect() para comprobar si la memoria realmente no está siendo ayudada por nadie. parece que es así hasta que se cierre el paquete. sí, llamamos al paquete cuando ya no lo necesitamos. el problema es que es la fuente principal de datos en nuestra aplicación, por lo que debe permanecer abierta durante toda la vida útil de la aplicación. la transmisión PackagePart es algo que no necesitamos en todo momento. por eso esperamos que la memoria se libere a medida que dejamos el alcance del método ReadPackage(). tal vez el paquete tiene algún tipo de almacenamiento en caché interno ... – bjutus

Cuestiones relacionadas