Fuite de mémoire à l’aide de StreamReader et XmlSerializer

Je suis sur Google depuis quelques heures et j’ai essayé différentes choses, mais je ne peux pas sembler être au fond de cette …

Lorsque je lance ce code, l’utilisation de la mémoire augmente continuellement.

while (true) { try { foreach (ssortingng sym in ssortingnglist) { StreamReader r = new StreamReader(@"C:\Program Files\" + sym + ".xml"); XmlSerializer xml = new XmlSerializer(typeof(XMLObj), new XmlRootAtsortingbute("rootNode")); XMLObj obj = (XMLObj)xml.Deserialize(r); obj.Dispose(); r.Dispose(); r.Close(); } } catch(Exception ex) { Console.WriteLine(ex.ToSsortingng()); } Thread.Sleep(1000); Console.Clear(); } 

XMLObj est un object personnalisé

 [Serializable()] public class XMLObj: IDisposable { [XmlElement("block")] public List nodes{ get; set; } public XMLObj() { } public void Dispose() { nodes.ForEach(n => n.Dispose()); nodes= null; GC.SuppressFinalize(this); } } 

J’ai essayé d’append GC.Collect (); mais cela ne semble rien faire.

La fuite est ici:

 new XmlSerializer(typeof(XMLObj), new XmlRootAtsortingbute("rootNode")) 

XmlSerializer utilise la génération d’assembly et les assemblys ne peuvent pas être collectés. Il effectue une mise en cache / réutilisation automatique pour les scénarios de constructeur les plus simples ( new XmlSerializer(Type) , etc.), mais pas pour ce scénario. Par conséquent, vous devriez le mettre en cache manuellement:

 static readonly XmlSerializer mySerializer = new XmlSerializer(typeof(XMLObj), new XmlRootAtsortingbute("rootNode")) 

et utilisez l’instance de sérialiseur mise en cache.

Tout d’abord, vous devriez vous débarrasser de votre StreamReader même si une exception est levée (idem pour XMLObj). Utilisez l’instruction using . Actuellement, vous ne disposerez pas lorsqu’une exception est levée.

Il est très peu probable que vous ayez une fuite de mémoire. Plus probablement, le moteur d’exécution n’a simplement pas choisi de collecter de la mémoire pour le moment. Même GC.Collect ne provoquera pas nécessairement la libération de mémoire.

J’ai rencontré des situations similaires lors du traitement de très gros fichiers XML (multi-Go). Même si le moteur d’exécution saisit la plus grande partie de la mémoire disponible, il la libère lorsque la pression de la mémoire le justifie.

Vous pouvez utiliser le profileur de mémoire dans Visual Studio pour voir quelle mémoire est allouée et dans quelle génération elle réside.

METTRE À JOUR

Le commentaire de @KaiEichinger mérite d’être étudié. Il indique que XmlSerializer est en train de créer une nouvelle définition d’object mis en cache pour chaque itération de boucle.

Le constructeur XMLSerializer crée l’assemblage temporaire pour le type à sérialiser à l’aide de la reflection et, comme la génération de code est coûteuse, l’assemblage est mis en cache dans la mémoire, type par type. Mais souvent, le nom de la racine sera modifié et peut être dynamic sans pour autant mettre en cache l’assembly dynamic. Ainsi, chaque fois que la ligne de code ci-dessus est appelée, elle charge le nouvel assemblage à chaque fois et rest en mémoire jusqu’à ce que AppDomain soit déchargé.

De MSDN: entrez la description du lien ici

Pour améliorer les performances, l’infrastructure de sérialisation XML génère de manière dynamic des assemblys pour sérialiser et désérialiser les types spécifiés. L’infrastructure trouve et réutilise ces assemblages. Ce problème se produit uniquement lors de l’utilisation des constructeurs suivants:

XmlSerializer.XmlSerializer (Type)

XmlSerializer.XmlSerializer (Type, Chaîne)

Si vous utilisez l’un des autres constructeurs, plusieurs versions du même assemblage sont générées et jamais déchargées, ce qui entraîne une fuite de mémoire et des performances médiocres. La solution la plus simple consiste à utiliser l’un des deux constructeurs mentionnés précédemment. Sinon, vous devez mettre en cache les assemblys dans une table de hachage, comme illustré dans l’exemple suivant.

=> Donc, pour résoudre ce problème, vous devez utiliser ce constructeur XmlSerializer xml = new XmlSerializer(typeof(XMLObj)) au lieu de XmlSerializer xml = new XmlSerializer(typeof(XMLObj), new XmlRootAtsortingbute("rootNode"));

et append un atsortingbut XML racine dans la classe XMLObj.

 [Serializable()] [XmlRoot("root")] public class XMLObj: IDisposable { [XmlElement("block")] public List nodes{ get; set; } public XMLObj() { } public void Dispose() { nodes.ForEach(n => n.Dispose()); nodes= null; GC.SuppressFinalize(this); } } 

J’utilise une classe “cache” pour éviter d’instancier xmlserializer chaque fois que vous devez sérialiser quelque chose (j’ai également ajouté un XmlCommentAtsortingbute pour append des commentaires aux propriétés sérialisées dans la sortie xml). Pour moi, cela fonctionne comme sharm. J’espère pouvoir aider quelqu’un avec cela. :

  public static class XmlSerializerCache { private static object Locker = new object(); private static Dictionary SerializerCacheForUtils = new Dictionary(); public static XmlSerializer GetSerializer() { return GetSerializer(null); } public static XmlSerializer GetSerializer(Type[] ExtraTypes) { return GetSerializer(typeof(T), ExtraTypes); } public static XmlSerializer GetSerializer(Type MainTypeForSerialization) { return GetSerializer(MainTypeForSerialization, null); } public static XmlSerializer GetSerializer(Type MainTypeForSerialization, Type[] ExtraTypes) { ssortingng Signature = MainTypeForSerialization.FullName; if (ExtraTypes != null) { foreach (Type Tp in ExtraTypes) Signature += "-" + Tp.FullName; } XmlSerializer XmlEventSerializer; if (SerializerCacheForUtils.ContainsKey(Signature)) XmlEventSerializer = SerializerCacheForUtils[Signature]; else { if (ExtraTypes == null) XmlEventSerializer = new XmlSerializer(MainTypeForSerialization); else XmlEventSerializer = new XmlSerializer(MainTypeForSerialization, ExtraTypes); SerializerCacheForUtils.Add(Signature, XmlEventSerializer); } return XmlEventSerializer; } public static T Deserialize(XDocument XmlData) { return Deserialize(XmlData, null); } public static T Deserialize(XDocument XmlData, Type[] ExtraTypes) { lock (Locker) { T Result = default(T); try { XmlReader XmlReader = XmlData.Root.CreateReader(); XmlSerializer Ser = GetSerializer(ExtraTypes); Result = (T)Ser.Deserialize(XmlReader); XmlReader.Dispose(); return Result; } catch (Exception Ex) { throw new Exception("Could not deserialize to " + typeof(T).Name, Ex); } } } public static T Deserialize(ssortingng XmlData) { return Deserialize(XmlData, null); } public static T Deserialize(ssortingng XmlData, Type[] ExtraTypes) { lock (Locker) { T Result = default(T); try { using (MemoryStream Stream = new MemoryStream()) { using (StreamWriter Writer = new StreamWriter(Stream)) { Writer.Write(XmlData); Writer.Flush(); Stream.Position = 0; XmlSerializer Ser = GetSerializer(ExtraTypes); Result = (T)Ser.Deserialize(Stream); Writer.Close(); } } return Result; } catch (Exception Ex) { throw new Exception("Could not deserialize to " + typeof(T).Name, Ex); } } } public static XDocument Serialize(T Object) { return Serialize(Object, null); } public static XDocument Serialize(T Object, Type[] ExtraTypes) { lock (Locker) { XDocument Xml = null; try { using (MemoryStream stream = new MemoryStream()) { XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); ns.Add("", ""); using (StreamReader Reader = new StreamReader(stream)) { XmlSerializer Serializer = GetSerializer(ExtraTypes); var settings = new XmlWriterSettings { Indent = true }; using (var w = XmlWriter.Create(stream, settings)) { Serializer.Serialize(w, Object, ns); w.Flush(); stream.Position = 0; } Xml = XDocument.Load(Reader, LoadOptions.None); foreach (XElement Ele in Xml.Root.Descendants()) { PropertyInfo PI = typeof(T).GetProperty(Ele.Name.LocalName); if (PI != null && PI.IsDefined(typeof(XmlCommentAtsortingbute), false)) Xml.AddFirst(new XComment(PI.Name + ": " + PI.GetCustomAtsortingbutes(typeof(XmlCommentAtsortingbute), false).Cast().Single().Value)); } Reader.Close(); } } return Xml; } catch (Exception Ex) { throw new Exception("Could not serialize from " + typeof(T).Name + " to xml ssortingng", Ex); } } } } [AtsortingbuteUsage(AtsortingbuteTargets.Property, AllowMultiple = false)] public class XmlCommentAtsortingbute : Atsortingbute { public ssortingng Value { get; set; } } 

Je pense que déplacer le constructeur XMLSerializer dehors de la boucle et mettre en cache son résultat va le réparer, explication ici