反序列化字典上的 c# 内存泄漏
c# memory leak on Deserialize dictionary
当我使用该方法反序列化字典时,我发生了内存泄漏。此处进行以下测试以重现问题。
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>();
}
}
你知道问题出在哪里吗?
编辑:我在循环中(大约 20000)在 workerazure 角色中使用此方法,这会填充内存并抛出内存不足异常。
与反序列化无关,与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 网站上的支持文章。
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 Whosebug
据我所知,没有内存泄漏。但是在你的测试用例中有一个明显的错误内存使用。
CG.Collect() 大多数时候并不是迫在眉睫。特别是当有非托管资源时,它必须在释放所有不需要的内存之前清除终结队列。
在这种情况下,它会消耗大量内存并且不允许垃圾收集器完成其终结过程。
因此,反序列化后,您可以调用 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();
参考:
当我使用该方法反序列化字典时,我发生了内存泄漏。此处进行以下测试以重现问题。
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>();
}
}
你知道问题出在哪里吗?
编辑:我在循环中(大约 20000)在 workerazure 角色中使用此方法,这会填充内存并抛出内存不足异常。
与反序列化无关,与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 网站上的支持文章。
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 Whosebug
据我所知,没有内存泄漏。但是在你的测试用例中有一个明显的错误内存使用。
CG.Collect() 大多数时候并不是迫在眉睫。特别是当有非托管资源时,它必须在释放所有不需要的内存之前清除终结队列。
在这种情况下,它会消耗大量内存并且不允许垃圾收集器完成其终结过程。
因此,反序列化后,您可以调用 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();
参考: