Fuga de memoria usando StreamReader y XmlSerializer

He estado buscando en Google durante las últimas horas y probando diferentes cosas, pero no puedo ver el fondo de esto …

Cuando ejecuto este código, el uso de memoria crece continuamente.

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

XMLObj es un objeto personalizado

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

He intentado agregar GC.Collect (); pero eso no parece hacer nada.

La filtración está aquí:

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

XmlSerializer utiliza la generación de ensamblaje y los ensamblados no se pueden recostackr. Hace un poco de caché / reutilización automático para los escenarios de constructor más simples ( new XmlSerializer(Type) , etc.), pero no para este escenario. En consecuencia, debe almacenarlo en caché manualmente:

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

y use la instancia del serializador en caché.

En primer lugar, debe deshacerse de su StreamReader incluso si se lanza una excepción (lo mismo para XMLObj). Usa la instrucción using . Actualmente no se deshará cuando se lanza una excepción.

Es muy poco probable que tenga una pérdida de memoria. Lo más probable es que el tiempo de ejecución simplemente no eligió recostackr memoria todavía. Incluso GC.Collect no necesariamente liberará memoria.

Me he encontrado con situaciones similares al procesar archivos XML de gran tamaño (multi-GB). Aunque el tiempo de ejecución toma la mayoría de la memoria disponible, la libera cuando la memoria lo amerita.

Puede usar el generador de perfiles de memoria en Visual Studio para ver qué memoria está asignada y en qué generación reside.

ACTUALIZAR

El comentario de @KaiEichinger vale la pena investigar. Indica que el XmlSerializer puede estar creando una nueva definición de objeto en caché para cada iteración de bucle

El constructor de XMLSerializer crea el ensamblaje temporal para el tipo que se serializará mediante la reflexión y, dado que la generación de código es costosa, el ensamblado se guarda en la memoria en memoria caché por tipo. Pero muchas veces el nombre de la raíz cambiará y puede ser dynamic y no almacenará en caché el ensamblaje dynamic. Por lo tanto, cada vez que se llame a la línea de código anterior, cargará el ensamblaje nuevo cada vez y permanecerá en la memoria hasta que se descargue AppDomain.

Desde MSDN: ingrese la descripción del enlace aquí

Para boost el rendimiento, la infraestructura de serialización XML genera dinámicamente conjuntos para serializar y deserializar tipos especificados. La infraestructura encuentra y reutiliza esos conjuntos. Este comportamiento solo ocurre cuando se usan los siguientes constructores:

XmlSerializer.XmlSerializer (Type)

XmlSerializer.XmlSerializer (Type, String)

Si utiliza cualquiera de los otros constructores, se generan múltiples versiones del mismo ensamblaje y nunca se descargan, lo que da como resultado una pérdida de memoria y un rendimiento deficiente. La solución más fácil es usar uno de los dos constructores mencionados anteriormente. De lo contrario, debe almacenar en caché los ensamblajes en una tabla Hashtable, como se muestra en el siguiente ejemplo.

=> Entonces, para arreglarlo debes usar este constructor XmlSerializer xml = new XmlSerializer(typeof(XMLObj)) lugar de XmlSerializer xml = new XmlSerializer(typeof(XMLObj), new XmlRootAttribute("rootNode"));

y agrega el atributo raíz de XML a la clase 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); } } 

Estoy usando una clase de “caché” para evitar crear instancias de xmlserializer cada vez que necesita serializar algo (también agregó un XmlCommentAttribute para agregar comentarios a las propiedades serializadas en el resultado xml), para mí funciona como sharm, espero ayudar a alguien con esto :

  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) { string 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(string XmlData) { return Deserialize(XmlData, null); } public static T Deserialize(string 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(XmlCommentAttribute), false)) Xml.AddFirst(new XComment(PI.Name + ": " + PI.GetCustomAttributes(typeof(XmlCommentAttribute), false).Cast().Single().Value)); } Reader.Close(); } } return Xml; } catch (Exception Ex) { throw new Exception("Could not serialize from " + typeof(T).Name + " to xml string", Ex); } } } } [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public class XmlCommentAttribute : Attribute { public string Value { get; set; } } 

Creo que mover el constructor de XMLSerializer fuera del bucle y almacenar en caché su resultado lo arreglará, explicación aquí