Warum verursacht die Instanziierung von XmlSerializer vor dem Aufruf von ServiceHost.Open ein Speicher- und Handle-Leck

8

Bei der Suche nach einem Speicher- und Handle-Leak in einem .NET / WCF / Windows-Dienst habe ich seltsames Verhalten bemerkt, das ich nicht erklären kann. Hier das Setup und die Auflösung. Wonach ich suche, wäre eine Erklärung für das beobachtete Verhalten.

Ich habe einen Windows-Dienst installiert.
Ich habe den Dienst begonnen.
Ich habe eine einfache Methode mit einem transaktionalen WCF-Aufruf aufgerufen (neuer Kanal pro Aufruf - kein Caching).
Für jeden Anruf verbleiben etwa 2 Handles im Speicher.

Dies kann beobachtet werden, wenn die folgenden Punkte anwendbar sind:

  1. Es ist ein Windows-Dienst; führe es nicht als Konsolen-App aus.
  2. Verwenden Sie eine Transaktion (separater Prozess oder nur getestete Maschine), um die WCF-Methode aufzurufen.
  3. Bevor Sie ServiceBase.Run(servicesToRun); aufrufen, instanziieren Sie XmlSerializer mit einem Typ.
  4. Der Typ ist ein benutzerdefinierter Typ. Es tritt nicht mit new XmlSerializer(typeof(string)) oder new XmlSerializer(typeof(XmlDocument)) auf. Kein Aufruf zum Serialisieren ist notwendig. Es reicht aus, wenn der benutzerdefinierte Typ nur eine Zeichenfolge als Eigenschaft hat (keine Handles irgendwo!)
  5. Das Erstellen einer statischen XmlSerialization.dll mit d. d. SGen.exe wird dieses Problem nicht verursachen.

Mein Code enthält bereits das Update:
Verwenden Sie XmlSerializer früheste in OnStart ():

Programm.cs

%Vor%

WindowsService.cs

%Vor%

DemoService.cs

%Vor%

Client.cs :

%Vor%

Kann jemand dieses Verhalten erklären?

Bitte beachten Sie, ich bin nicht daran interessiert, einen Weg zu finden, das Leck zu vermeiden (ich weiß bereits, wie man das macht), aber in einer Erklärung (d. h. WARUM geschieht es).

    
StampedeXV 26.10.2012, 11:23
quelle

2 Antworten

7

Ich denke, dass einige der inneren Abläufe dieser Frage gerecht werden. Ich mache das von meinem Hinterkopf aus, da ich vor einiger Zeit auf dieses Problem gestoßen bin, und es dauerte einen Tag, bis ich zurückging, einschließlich der umfangreichen Verwendung von Reflector und ANTS Memory Profiler (bei meiner vorherigen Firma) ... hier geht:

Was der XML-Serializer intern tut, ist die Erstellung einer Klasse (nennen wir sie 'A') mit System.Reflection.Emit, das den Typ akzeptiert, den Sie an ihn übergeben. Das Konstruieren einer solchen Klasse kostet relativ viel Zeit und ist wiederverwendbar, da sich die Typen nicht ändern. Aus diesem Grund werden die konstruierten Typen in einem Wörterbuch gespeichert, z. es endet mit einem Wörterbuch.

Für bekannte (grundlegende) Typen ist der Serialisierungscode fest, z. Die Serialisierung eines Strings wird sich nicht ändern, egal wie oft Sie Ihre Anwendung neu starten. Beachten Sie den Unterschied zu 'A', bei dem jeder Typ, der der Serialisierungsfactory unbekannt ist, bis zum ersten Mal an den XMLSerializer übergeben wird.

Wenn der Typ zum ersten Mal vom XMLSerializer verwendet wird, findet dieser Prozess sowohl für den übergebenen Typ als auch für alle Typen statt, die er benötigt (z. B. alle Felder und Eigenschaften, die serialisiert werden müssen).

Über das Leck ... Wenn Sie die ChannelFactory aufrufen, baut sie den Serializer auf, falls er noch nicht existiert. Dazu prüft es, ob der Serializer bereits im Dictionary vorhanden ist und wenn nicht, erstellt er einen, indem er eine Instanz von ISomeSerializerType erstellt.

Aus irgendeinem dummen Grund gibt es einen Fehler in der Fabrik, der einen neuen Serializer erstellt, ohne ihn im Wörterbuch zu speichern. Nach der Erstellung erhalten Sie einen neuen Typ - der sich als Leck zeigt (denken Sie daran: Typen können nie entladen werden) - obwohl die Objekte korrekt entsorgt werden. Wenn Sie XMLSerializer zuerst verwenden oder eine statische Klasse erstellen, wird der Dictionary-Cache korrekt verwendet, was bedeutet, dass er nicht leckt. Also da hast du es, es ist ein Bug . Ich hatte früher Zugriff auf den ANTS Memory Profiler, was das sehr schön zeigte.

Ich hoffe, dies erklärt.

    
atlaste 09.01.2013, 19:37
quelle
6

Die XmlSerializer-Dokumentation sagt dies:

  

Um die Leistung zu erhöhen, generiert die XML-Serialisierungsinfrastruktur dynamisch Assemblys, um bestimmte Typen zu serialisieren und zu deserialisieren. Die Infrastruktur findet und wiederverwendet diese Assemblys. Dieses Verhalten tritt nur bei Verwendung der folgenden Konstruktoren auf:

     

XmlSerializer.XmlSerializer (Typ)

     

XmlSerializer.XmlSerializer (Typ, String)

     

Wenn Sie einen der anderen Konstruktoren verwenden, werden mehrere Versionen derselben Assembly generiert und nie entladen, was zu einem Speicherverlust und einer schlechten Leistung führt. Die einfachste Lösung besteht darin, einen der zuvor genannten zwei Konstruktoren zu verwenden. Andernfalls müssen Sie die Assemblys in einer Hashtable zwischenspeichern, wie im folgenden Beispiel gezeigt.

Ссылка

    
ta.speot.is 26.10.2012 12:28
quelle