can anyone tell us what's wrong with this code below? we have here an object serializer that should return an XML string of any object passed to it.
we've been scratching our heads over this one, as we have a program that calls this multiple times and we see our memory usage going sky high (and staying there even after the program was done).. we've done searches but to no avail. the stream object is inside a "using" statement so we thought this was supposed to be disposed on its own.. help please.
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 SerializerFactory looks like this:
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;
}
}
There is nothing hugely wrong with that code; note that using
is essentially a no-op on a MemoryStream
, since it only has managed resources, and managed resources are the domain of the GC; it is normal for the GC not to worry hugely until it makes sense to collect some memory, so I wouldn't stress too much - or: if there is a problem, it probably isn't this.
One observation, though, would be that you can avoid a buffer in the encode step:
var s = Encoding.ASCII.GetString(stream.GetBuffer(), 0, (int)stream.Length);
and actually, I'd be tempted to use UTF8 by default, not ASCII.
A final thought: is your SerializerFactory
itself doing something that leaks? For example, are you creating a new XmlSerializer(...)
via any of the more complex constructors? The simplest form:
new XmlSerializer(typeof(SomeType));
is fine - it internally caches the internal/actual serializer per-type, and re-uses this for each XmlSerializer
instance created this way. However, it does not do this caching for the more complex constructor overloads: it creates and loads a new dynamic assembly each time. And assemblies loaded in this way are never unloaded - so yes, that can cause a memory leak. I would be very keen to see how the serializer instances are created, to see if that is the actual problem. Note that such cases are usually very easy to fix by creating your own serializer-cache in the factory:
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;
}
}
}
Memory leak does not happen on MemoryStream
, it actually happens on XmlSerializer
:
“This overload of the XmlSerializer constructor does not cache the dynamically generated assembly, but generates a new temporary assembly every time you instantiate a new XmlSerializer! The app is leaking unmanaged memory in the form of temporary assemblies.”
Take a look on this article:
http://msdn.microsoft.com/en-us/magazine/cc163491.aspx
Instead of creating XmlSerializer
everytime, you need to cache for each type, to solve your problem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With