2010-09-29 31 views
5

Dans notre application, nous lisons un fichier XPS à l'aide de la classe System.IO.Packaging.Package. Lorsque nous lisons à partir d'un flux d'un packagePart, nous pouvons voir à partir du Gestionnaire des tâches que la consommation de mémoire de l'application augmente. Cependant, lorsque la lecture est terminée, la consommation de mémoire ne revient pas à ce qu'elle était avant de lire le flux. Pour illustrer le problème, j'ai écrit un exemple de code simple que vous pouvez utiliser dans une application wpf autonome.La lecture du flux PackagePart ne libère pas la mémoire

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

La méthode ReadPackage() lit tout le contenu du flux des objets PackagePart dans un tableau local. Dans l'exemple, j'ai utilisé un document XPS de 1000 pages comme source de paquet afin de voir facilement le changement de consommation de mémoire de l'application. Sur ma machine, la consommation de mémoire de l'application autonome commence à 18 Mo puis s'élève à 100 Mo après l'appel de la méthode. Appeler la méthode à nouveau peut augmenter la consommation de mémoire à nouveau, mais il peut revenir à 100 Mo. Cependant, il ne retombe plus à 18 Mo.

Quelqu'un at-il déjà rencontré ce problème lors de l'utilisation de PackagePart? Ou est-ce que je l'utilise mal? Je pense que l'implémentation interne de PackagePart met en cache les données qui ont été lues.

Merci!

+0

Je ne sais pas pourquoi cette question a été déclassée. –

Répondre

0

Vous ne spécifiez pas comment vous mesurez la "consommation de mémoire" de votre application, mais vous utilisez peut-être le gestionnaire de tâches? Pour avoir une meilleure vue de ce qui se passe, je vous suggère d'examiner quelques compteurs de performance pour votre application. Les compteurs de performances de la mémoire .NET et du processus général sont disponibles.

Si vous voulez vraiment comprendre les détails de la façon dont votre application utilise la mémoire, vous pouvez utiliser le Microsoft CLR profiler.

Ce que vous voyez peut être le résultat de l'expansion du segment .NET pour prendre en charge un très gros fichier. Les gros objets sont placés sur le gros tas d'objets (LOH) et même si la mémoire .NET est récupérée, la mémoire libre n'est jamais retournée au système d'exploitation. De plus, les objets sur le LOH ne sont jamais déplacés pendant le garbage collection et ceci peut fragmenter le LOH épuisant l'espace d'adressage disponible même s'il y a beaucoup de mémoire libre.

Quelqu'un at-il déjà rencontré cette erreur lors de l'utilisation de PackagePart? Ou est-ce que je l'utilise mal?

Si vous souhaitez contrôler les ressources utilisées par le package, vous ne l'utilisez pas au mieux. Les paquets sont à usage unique et en général, vous devriez l'utiliser comme ceci:

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

A la fin des ressources des états using consommés par le paquet sont soit déjà sorti ou peuvent être garbage collection.

Si vous souhaitez vraiment conserver le membre _package de votre formulaire, vous devez à un moment donné appeler le Close() (ou IDisposable.Dispose()) pour libérer les ressources. Il n'est pas recommandé d'appeler le GC.Collect() et vous ne pourrez pas nécessairement recycler les ressources utilisées par le package. Toute mémoire gérée (par exemple, les tampons de package) accessible à partir de _package ne sera pas collectée sans tenir compte de la fréquence à laquelle vous essayez de forcer un nettoyage de place.

+0

merci pour la réponse! J'utilisais TaskManager. yup va essayer le profileur CLR comme une autre option.Ce qui m'inquiète, c'est de perdre du temps à essayer de trouver une solution qui est en fait un bogue dans le code d'implémentation interne de PackagePart que seul Microsoft peut corriger. J'ai également essayé de remplacer le flux PackagePart par un FileStream qui lit le contenu d'un fichier de 1 Mo dans le tableau. Ceci est fait le même nombre de fois que le code ci-dessus. C'est fondamentalement la même procédure mais seulement la lecture d'un flux différent. Dans ce cas, la mémoire est en cours de collecte. Il n'a même pas atteint 50MB. – bjutus

+0

hmm. J'ai essayé d'appeler GC.Collect() juste après avoir appelé ReadPackage() mais rien ne s'est passé. cependant, j'ai appelé _package.Close() puis GC.Collect() après ReadPackage() et l'utilisation de la mémoire est redescendue à environ 20 Mo à partir de 100 Mo. alors peut-être que le paquet contenait les références des flux? – bjutus

+0

vu vos mises à jour dans votre answer.thanks à nouveau. J'ai seulement essayé GC.Collect() pour vérifier si la mémoire n'est vraiment aidée par personne. On dirait que c'est le cas jusqu'à ce que le paquet soit fermé. Oui, nous appelons à proximité sur le paquet quand nous n'en avons plus besoin. le problème est que c'est la principale source de données dans notre application, donc il doit rester ouvert pendant toute la durée de vie de l'application. le flux PackagePart est cependant quelque chose dont nous n'avons pas besoin tout au long. C'est pourquoi nous nous attendons à ce que la mémoire soit libérée lorsque nous quittons la portée de la méthode ReadPackage(). peut-être le paquet a une sorte de mise en cache interne ... – bjutus