2012-09-07 19 views
6

¿alguien puede decirnos qué le pasa a este código? tenemos aquí un serializador de objetos que debe devolver una cadena XML de cualquier objeto que se le pase.MemoryStream leak

nos hemos estado rascando la cabeza por esto, ya que tenemos un programa que llama a esto varias veces y vemos que nuestro uso de memoria se dispara (y permanecer allí incluso después de que el programa haya terminado) .. búsquedas hechas, pero fue en vano. el objeto de transmisión está dentro de una declaración de "uso", por lo que pensamos que se suponía que debía eliminarse por sí solo ... ayuda, por favor.

public static string ToXML(this IMessage m) 
    {   
     try 
     { 
      var serializer = SerializerFactory.Create(m.GetType()); 
      using (var stream = new MemoryStream()) 
      { 
       serializer.Serialize(new[] { m }, stream); 
       stream.Position = 0; 
       var s = Encoding.ASCII.GetString(stream.ToArray()); 
       return s; 
      } 
     } 
     catch (Exception e) 
     { 
      return string.Format("Message unserializable: {0}", e.Message); 
     } 
    } 

por cierto SerializerFactory se parece a esto:

public class SerializerFactory 
{ 
    public static IMessageSerializer Create(Type t) 
    { 
     var types = new List<Type> { t }; 
     var mapper = new MessageMapper(); 
     mapper.Initialize(types); 
     var serializer = new XmlMessageSerializer(mapper); 

     serializer.Initialize(types); 

     return serializer; 
    } 
} 
+1

Quizás no haya presión de memoria, por lo que el GC simplemente se está tomando las cosas con calma. – ChaosPandion

+0

No parece haber nada obviamente incorrecto aquí. ¿Has intentado ejecutarlo a través de un generador de perfiles de memoria? – Dervall

+0

¿Alguna excepción que se arroje? Está pasando el flujo a la llamada de serializador. Así que estoy teniendo un poco de duda, me refiero a que el tiempo de vida de las transmisiones se extiende mucho más allá de su entorno de uso(). – Zenwalker

Respuesta

6

No hay nada enormemente mal con ese código; tenga en cuenta que using es esencialmente un no-op en un MemoryStream, ya que solo tiene recursos administrados, y los recursos administrados son el dominio del GC; es normal que el GC no se preocupe demasiado hasta que tenga sentido para recopilar algo de memoria, así que no me estresaría demasiado, o si hay un problema, probablemente no sea esto.

Una observación, sin embargo, sería que se puede evitar un búfer en la etapa de codificación:

var s = Encoding.ASCII.GetString(stream.GetBuffer(), 0, (int)stream.Length); 

y, de hecho, estaría tentado a usar UTF8 por defecto, no ASCII.

Pensamiento final: es su SerializerFactoryen sí haciendo algo que se filtra? Por ejemplo, ¿está creando un new XmlSerializer(...) a través de cualquiera de los constructores más complejos? La forma más simple:

new XmlSerializer(typeof(SomeType)); 

es bien - que almacena internamente la interna/real serializador por tipo, y vuelve a utilizar esto para cada XmlSerializer instancia creada de esta manera. Sin embargo, hace no hacer este almacenamiento en caché para las sobrecargas de constructor más complejas: crea y carga un nuevo conjunto dinámico cada vez. Y los ensamblados cargados de esta manera nunca se descargan - entonces sí, que puede causar una pérdida de memoria. Me gustaría ver cómo se crean las instancias del serializador, para ver si que es el problema real. Tenga en cuenta que estos casos suelen ser muy fácil de solucionar mediante la creación de su propio serializador-caché en la fábrica:

public class SerializerFactory 
{ 
    // hashtable has better threading semantics than dictionary, honest! 
    private static readonly Hashtable cache = new Hashtable(); 
    public static IMessageSerializer Create(Type t) 
    { 
     var found = (IMessageSerializer)cache[t]; 
     if(found != null) return found; 

     lock(cache) 
     { // double-checked 
      found = (IMessageSerializer)cache[t]; 
      if(found != null) return found; 

      var types = new List<Type> { t }; 
      var mapper = new MessageMapper(); 
      mapper.Initialize(types); 
      var serializer = new XmlMessageSerializer(mapper); 

      serializer.Initialize(types); 

      cache[t] = serializer; 

      return serializer; 
     } 
    } 
} 
+0

ha agregado SerializerFactory como edición. gracias y vamos a echar un vistazo en este – user1307017

+0

@ user1307017 editado para agregar un serializador-caché a su método 'Create' –

+0

HOLY CRAP ... la modificación hizo toda la magia. tenías razón, estábamos bombardeando nuestro programa usando el código anterior sin almacenar nada en caché ... como en 100.000 veces ... estábamos creando ensamblajes dinámicamente ... ahora aplicamos esa solución, y todo es perfecto. ¡¡¡Gracias!!! – user1307017

3

Pérdida de memoria no sucede en MemoryStream, lo que realmente sucede en XmlSerializer:

“Esta la sobrecarga del constructor XmlSerializer no almacena en caché el ensamblado generado dinámicamente, ¡sino que genera un nuevo ensamblaje temporal cada vez que instancia un nuevo XmlSerializer! La aplicación está filtrando memoria no administrada en forma de ensambles temporales.”

echar un vistazo a este artículo:

http://msdn.microsoft.com/en-us/magazine/cc163491.aspx

En lugar de crear XmlSerializer cada vez, es necesario caché para cada tipo, para resolver su problema.

+0

gracias por la explicación, esto es similar con el de Marc. funcionó y aprendimos algo nuevo hoy – user1307017