反序列化字典中的c#内存泄漏

时间:2015-03-04 19:03:01

标签: c# .net dictionary memory-leaks garbage-collection

当我使用该方法反序列化字典时,我有内存泄漏。这里通过以下测试重现问题。

public static Dictionary<TKey, TValue> DeserializeDictionary<TKey, TValue>(this string iSerialization)
    {
        Dictionary<TKey, TValue> dic;

        using (var textWriter = new StringReader(iSerialization))
        {
            XmlSerializer serializer = new XmlSerializer(typeof(Item<TKey, TValue>[]), new XmlRootAttribute() { ElementName = "items" });
            dic = ((Item<TKey, TValue>[])serializer.Deserialize(textWriter)).ToDictionary(i => i.Key, i => i.Value);
            textWriter.Close();
        }
        return dic;
    }

public class Item<TKey, TValue>
    {
        [XmlAttribute]
        public TKey Key;

        [XmlAttribute]
        public TValue Value;
    }

测试:

[TestMethod]
    public void test()
    {
        string test = "<?xml version=\"1.0\" encoding=\"utf-16\"?><items xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><ItemOfStringString Key=\"mykey\" Value=\"myvalue\" /></items>";

        while(true)
        {
            Dictionary<string, string> tesfezf = test.DeserializeDictionary<string, string>();
        }

    }

你知道问题出在哪里吗?

编辑:我在一个循环(arround 20000)中以workerazure角色使用这个方法,这填补了内存并抛出了内存异常。

2 个答案:

答案 0 :(得分:2)

它与反序列化无关,而是与XmlSerializer的实例化无关。此特定构造函数重载生成一个新的临时程序集,该程序集在每次调用时都会在应用程序域中加载,并且永远不会被卸载。最终结果是,如果计划在应用程序域的生命周期中多次使用它,则应缓存XmlSerializer或使用其他构造函数。

您可以使用这段代码对其进行测试,您将看到内存使用量的增加。

var root = new XmlRootAttribute() {ElementName = "items"};
var type = typeof (Item<string, string>[]);
while (true)
{
    XmlSerializer serializer = new XmlSerializer(type, root);
    GC.Collect();
}

如果将块 XmlSerializer serializer = new XmlSerializer(type,root); 移出代码中的循环,只是在循环中反序列化,则内存保持不变。

以下是大量文章都描述了相同的问题,包括微软网站上的支持文章。

Microsoft Support KB 886385

Blog on Msdn - .NET Memory Leak: XmlSerializing your way to a Memory Leak

A Memory Leak brought to you by XmlSerializer

Question and Answer on StackOverflow

答案 1 :(得分:0)

据我所知,没有内存泄漏。但是在你的测试用例中存在明显不良的内存使用情况。

CG.Collect()大部分时间都不是迫在眉睫的。特别是当存在非托管资源时,它必须在释放所有不需要的内存之前清除终结队列。

在这种情况下,它会消耗大量内存,并且不允许垃圾收集器完成其最终化过程。

因此,在Deserialize之后,您可以调用GC.Collect()并等待GC完成其最终确定。

   //Force garbage collection.
   GC.Collect();

   // Wait for all finalizers to complete before continuing. 
   // Without this call to GC.WaitForPendingFinalizers,  
   // the worker loop below might execute at the same time  
   // as the finalizers. 
   // With this call, the worker loop executes only after 
   // all finalizers have been called.
   GC.WaitForPendingFinalizers();

参考:

GC.WaitForPendingFinalizers

GC.WaitForFullGCComplete Method