7

谁能告诉我们下面这段代码有什么问题?我们这里有一个对象序列化器,它应该返回传递给它的任何对象的 XML 字符串。

我们一直在为这个问题摸不着头脑,因为我们有一个程序多次调用它,并且我们看到我们的内存使用率很高(并且即使在程序完成后仍然存在)..我们已经完成了搜索但是徒劳无功。流对象位于“使用”语句中,因此我们认为这应该自行处理..请帮助。

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

顺便说一句 SerializerFactory 看起来像这样:

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

2 回答 2

7

该代码没有什么问题。请注意,这using本质是对 a 的无操作MemoryStream,因为它只有托管资源,而托管资源是 GC 的域;在收集一些内存有意义之前,GC 不会非常担心是正常的,所以我不会过分强调 - 或者:如果有问题,可能不是这个。

不过,一个观察结果是您可以在编码步骤中避免使用缓冲区:

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

实际上,我很想默认使用 UTF8,而不是 ASCII。

最后的想法:您SerializerFactory 自己是否在做一些泄漏的事情?例如,您是否new XmlSerializer(...)通过任何更复杂的构造函数创建一个?最简单的形式:

new XmlSerializer(typeof(SomeType));

很好- 它在内部缓存每种类型的内部/实际XmlSerializer序列化程序,并为以这种方式创建的每个实例重新使用它。但是,它不会更复杂的构造函数重载执行此缓存:它每次都创建并加载一个新的动态程序集。并且以这种方式加载的程序集永远不会被卸载- 所以是的,可能会导致内存泄漏。我很想看看序列化程序实例是如何创建的,看看是否是实际问题。请注意,这种情况通常很容易通过在工厂中创建自己的序列化程序缓存来解决:

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;
        }
    }
}
于 2012-09-07T06:51:50.103 回答
3

内存泄漏不会发生在MemoryStream,它实际上发生在XmlSerializer

“XmlSerializer 构造函数的这种重载不会缓存动态生成的程序集,而是每次实例化一个新的 XmlSerializer 时都会生成一个新的临时程序集!该应用程序正在以临时程序集的形式泄漏非托管内存。”</p>

看看这篇文章:

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

而不是每次都创建XmlSerializer,您需要为每种类型缓存,以解决您的问题。

于 2012-09-07T06:57:04.063 回答