serialize serializar deserialize deserializar convert c# xml streamreader xmlserializer

c# - serializar - Pérdida de memoria usando StreamReader y XmlSerializer



xml to list object c# (5)

Creo que mover el constructor XMLSerializer fuera del bucle y almacenar su resultado en caché lo solucionará, explicación here

He estado buscando en Google durante las últimas horas y probando cosas diferentes, pero no puedo llegar al fondo de esto ...

Cuando ejecuto este código, el uso de la 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<XMLnode> nodes{ get; set; } public XMLObj() { } public void Dispose() { nodes.ForEach(n => n.Dispose()); nodes= null; GC.SuppressFinalize(this); } }

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


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

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

XmlSerializer.XmlSerializer (Type)

XmlSerializer.XmlSerializer (Type, String)

Si utiliza cualquiera de los otros constructores, se generan varias versiones del mismo ensamblaje y nunca se descargan, lo que provoca 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 ensamblados en un Hashtable, como se muestra en el siguiente ejemplo.

=> Entonces, para solucionarlo, debe utilizar este constructor XmlSerializer xml = new XmlSerializer(typeof(XMLObj)) lugar de XmlSerializer xml = new XmlSerializer(typeof(XMLObj), new XmlRootAttribute("rootNode"));

y agregar el atributo XML raíz en la clase XMLObj.

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


En primer lugar, debe deshacerse de su StreamReader incluso si se lanza una excepción (igual para XMLObj). Utilice la instrucción using . Actualmente no dispondrás cuando se lance una excepción.

Es muy poco probable que tenga una pérdida de memoria. Más probable es que el tiempo de ejecución simplemente no eligió recopilar la memoria todavía. Incluso GC.Collect no necesariamente hará que se libere la memoria.

Me he encontrado en situaciones similares al procesar archivos XML muy grandes (multi-GB). Aunque el tiempo de ejecución toma la mayoría de la memoria disponible, la libera cuando la presión de la memoria lo requiere.

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 XMLSerializer crea el ensamblaje temporal para el tipo que se va a serializar mediante la reflexión y, como la generación de código es costosa, el ensamblaje se almacena en la memoria caché en base a cada tipo. Pero muchas veces el nombre de la raíz se cambiará y puede ser dinámico y no almacenará en caché el ensamblaje dinámico. Por lo tanto, siempre que se llame a la línea de código anterior, se cargará el nuevo ensamblaje cada vez y permanecerá en la memoria hasta que se descargue el dominio de aplicación.


Estoy usando una clase de "caché" para evitar la creación de instancias de xmlserializer cada vez que necesites serializar algo (también se agregó un XmlCommentAttribute para agregar comentarios a las propiedades serializadas en la salida 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<string, XmlSerializer> SerializerCacheForUtils = new Dictionary<string, XmlSerializer>(); public static XmlSerializer GetSerializer<T>() { return GetSerializer<T>(null); } public static XmlSerializer GetSerializer<T>(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<T>(XDocument XmlData) { return Deserialize<T>(XmlData, null); } public static T Deserialize<T>(XDocument XmlData, Type[] ExtraTypes) { lock (Locker) { T Result = default(T); try { XmlReader XmlReader = XmlData.Root.CreateReader(); XmlSerializer Ser = GetSerializer<T>(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<T>(string XmlData) { return Deserialize<T>(XmlData, null); } public static T Deserialize<T>(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<T>(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>(T Object) { return Serialize<T>(Object, null); } public static XDocument Serialize<T>(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<T>(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<XmlCommentAttribute>().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; } }


La fuga está aquí:

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

XmlSerializer utiliza la generación de ensamblajes y los ensamblajes no se pueden recopilar. Hace algo de caché / reutilización automática para los escenarios de constructor más simples ( new XmlSerializer(Type) , etc.), pero no para este escenario. En consecuencia, deberías cachearlo manualmente:

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

y usar la instancia del serializador en caché.