Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MemoryStream leak

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;
    }
}
like image 481
user1307017 Avatar asked Sep 07 '12 06:09

user1307017


2 Answers

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;
        }
    }
}
like image 79
Marc Gravell Avatar answered Sep 30 '22 15:09

Marc Gravell


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.

like image 27
cuongle Avatar answered Sep 30 '22 14:09

cuongle