2012-09-07 13 views
6

kann uns jemand sagen, was ist falsch mit diesem Code unten? Wir haben hier einen Objekt-Serializer, der eine XML-Zeichenfolge von jedem Objekt zurückgegeben sollte, die an es übergeben wird.MemoryStream Leck

wir haben uns über diesen Kopf gekratzt, da wir ein Programm haben, das dies mehrmals anruft und wir sehen, wie unser Speicherverbrauch in den Himmel steigt (und dort bleibt, auch nachdem das Programm fertig war) Suche durchgeführt, aber ohne Erfolg. Das stream-Objekt befindet sich in einer "using" -Anweisung, also dachten wir, dass dies auf eigene Faust geschehen sollte.

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

btw sieht SerializerFactory wie folgt aus:

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

Vielleicht gibt es keinen Speicherdruck, so dass der GC es einfach macht. – ChaosPandion

+0

Hier scheint nichts offensichtlich falsch zu sein. Haben Sie versucht, es über einen Speicherprofiler auszuführen? – Dervall

+0

Alle Ausnahmen werden ausgelöst? Sie übergeben den Stream an den Serializer-Aufruf. Also habe ich ein wenig Zweifel dort, ich meine über Streams Leben Zeit wird weit über seine Verwendung() Gebietsschema erweitert. – Zenwalker

Antwort

6

Es gibt nichts enorm falsch mit diesem Code; Beachten Sie, dass using ist im Wesentlichen ein No-Op auf einem MemoryStream, da es nur verwaltet Ressourcen hat, und verwaltete Ressourcen sind die Domäne des GC; es ist normal für den GC, sich nicht ernsthaft Sorgen zu machen, bis es Sinn macht, etwas Speicher zu sammeln, also würde ich nicht zu viel betonen - oder: wenn es ein Problem gibt, ist es wahrscheinlich nicht das.

Eine Beobachtung, aber wäre, dass Sie einen Puffer in dem Kodierungsschritt vermeiden:

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

und tatsächlich, würde ich versucht sein UTF8 der standardmäßig verwendet, nicht ASCII.

Ein letzter Gedanke: ist Ihr SerializerFactoryselbst etwas tun, das leckt? Erstellen Sie zum Beispiel einen new XmlSerializer(...) über einen der komplexeren Konstruktoren? Die einfachste Form:

new XmlSerializer(typeof(SomeType)); 

ist fein - es intern die interne/Ist Serializer pro-Typ-Caches und erneut verwendet diese für jede XmlSerializer Instanz auf diese Weise erstellt. Allerdings tut es nicht tun dieses Zwischenspeichern für die komplexere Konstruktor Überladungen: es erstellt und lädt eine neue dynamische Assembly jedes Mal. Und auf diese Weise geladene Baugruppen werden nie entladen - also ja, , dass Speicherverlust verursachen kann. Ich würde sehr gerne sehen, wie die Serializer-Instanzen erstellt werden, um zu sehen, ob das das eigentliche Problem ist. Beachten Sie, dass solche Fälle sind in der Regel sehr einfach zu beheben, indem Sie Ihre eigenen Serializer-Cache in der Fabrik zu schaffen:

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

hat die SerializerFactory als Bearbeitung hinzugefügt. Danke und wir werden uns das ansehen – user1307017

+0

@ user1307017 bearbeitet, um einen Serializer-Cache zu Ihrer 'Create' Methode hinzuzufügen –

+0

HOLY CRAP ... die Modifizierung hat all die Magie gemacht. Du hattest recht, wir haben unser Programm mit dem alten Code bombardiert, ohne irgendwas zwischenzuspeichern ... wie in 100.000 Mal .. wir haben dynamisch Assemblys erstellt .. jetzt haben wir diesen Fix angewendet, und alles ist perfekt. Vielen Dank!!! – user1307017

3

Speicherleck geschieht nicht auf MemoryStream, es passiert eigentlich auf XmlSerializer:

„Diese Die Überladung des XmlSerializer-Konstruktors führt nicht zum Zwischenspeichern der dynamisch generierten Assembly, sondern generiert jedes Mal, wenn Sie einen neuen XmlSerializer instanziieren, eine neue temporäre Assembly! Die App leckt nicht verwalteten Speicher in Form von temporären Assemblys.“

Werfen Sie einen Blick auf diesen Artikel:

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

Statt XmlSerializer jedes Mal zu schaffen, müssen Sie Cache für jeden Typ, Ihr Problem zu lösen.

+0

danke für die Erklärung, das ist ähnlich mit Marc. es hat funktioniert und wir haben heute etwas Neues gelernt – user1307017