Leaks with the XmlSerializer?

Kirk Allen Evans and Paul Wilson are investigating some strange behavior that Paul found using the XmlSerializer.

Make sure to read the follow-up.

Published Wednesday, February 11, 2004 9:39 PM by ChristophDotNet
Filed under: ,

Comments

# re: Leaks with the XmlSerilizer?

Wednesday, February 11, 2004 10:56 PM by Victor Garcia Aprea
Well I don't know about the "XmlSerilizer" but I've heard the "XmlSerializer" may be buggy ;-)

# re: Leaks with the XmlSerilizer?

Thursday, February 12, 2004 8:53 AM by Paul Wilson
Yes its a memory leak -- its NEVER reclaimed by the GC. Its the actual dynamic assembly that is "lost", which isn't managed memory. Some of the .net memory counters do increase a little, but its the private bytes of the process that go through the roof. Run this code snippet as the only file in a console app with performance monitor. With the leakMemory variable set to true, then run it again with leakMemory set to false. The only difference is whether or not the XmlSerializer is created each time, or only once. My last test had a fatal memory crash after leaking almost 500MB, which had long since pushed me far into the page file. All memory was reclaimed when the process was killed, or ended if you make it that far, but the GC never manages it or reclaims it. Can we switch to a simpler constructor? No. We could possibly use a different one with more work, but the other links I provided on my blog indicate that most of the constructors, except the very simplest, leak dynamic assemblies.

using System;
using System.IO;
using System.Text;
using System.Xml.Serialization;

namespace XmlLeak
{
class Global
{
private static bool leakMemory = true;
private static XmlSerializer serial = null;

[STAThread]
private static void Main(string[] args) {
for (int index = 0; index < 1000000; index++) {
Test test = new Test();
test.Id = index;
test.Time = DateTime.Now;
StringBuilder builder = new StringBuilder();
StringWriter writer = new StringWriter(builder);
if (leakMemory || serial == null) {
serial = new XmlSerializer(typeof(Base), new Type[] {typeof(Test)});
}
serial.Serialize(writer, test);
string xml = builder.ToString();
}
}
}

[Serializable()]
public class Test : Base
{
private DateTime time;
public DateTime Time {
get { return time; }
set { time = value; }
}
}

[Serializable()]
public class Base
{
private int id;
public int Id {
get { return id; }
set { id = value; }
}
}
}

# re: Leaks with the XmlSerializer?

Thursday, February 12, 2004 4:01 PM by Kirk Allen Evans
Looking at it through Reflector, the only two constructors that it is supported for are:

public XmlSerializer(Type type);
public XmlSerializer(Type type, string defaultNamespace);

The first constructor just forwards to the second with a null for the second parameter. The second ctor's body looks like (from Reflector):

XmlReflectionImporter importer1;
TempAssemblyCache cache1;
this.events = 0;
base..ctor();
this.events.sender = this;
this.tempAssembly = XmlSerializer.Cache[defaultNamespace,type];
if(this.tempAssembly == null)
{
cache1 = XmlSerializer.cache;
Monitor.Enter(XmlSerializer.cache);
try
{
this.tempAssembly = XmlSerializer.Cache[defaultNamespace,type];
if(this.tempAssembly == null)
{
importer1 = new XmlReflectionImporter(defaultNamespace);
this.tempAssembly = XmlSerializer.GenerateTempAssembly(importer1.ImportTypeMapping(type));
XmlSerializer.cache.Add(defaultNamespace,type,this.tempAssembly);
}
}
finally
{
Monitor.Exit(cache1);
}
}

The key for the cache (which is just a Hashtable) is the defaultNamespace that is passed in. Since the defaultNamespace is used for the key, that would not work for the following constructor signatures:

public XmlSerializer(Type type, Type[] extraTypes);
public XmlSerializer(Type type, System.Xml.Serialization.XmlAttributeOverrides overrides);
public XmlSerializer(Type type, System.Xml.Serialization.XmlRootAttribute root);
public XmlSerializer(System.Xml.Serialization.XmlTypeMapping xmlTypeMapping);

# Follow up: Workaround for XmlSerializer assembly leaks

Friday, January 14, 2005 10:41 PM by TrackBack

Leave a Comment

(required) 
(required) 
(optional)
(required)