关键字:XmlSerializer, Performace Issue
近日在解决一个性能问题时,发现如下这段代码的执行要花费较长的时间
SuperElement = XElement.Parse(xmlcontent from resource file);
// 获取指定的内容
IEnumerable<XElement> elements= SuperElement.Descendants().Where(
elem => elem.Name =="Element Name" &&
elem.Attribute("Target") != null);
//ObjectA就是要进行反序列化的类名
m_objectList = newList<ObjectA>();
// 遍历,然后逐一进行反序列化
int logCounter = 1;
foreach (XElement element inelements)
{
System.Diagnostics.Trace.TraceInformation("deserialize2.{0} begin", logCounter);
ObjectAobj = ObjectASerializer<ObjectA>.LoadFromXElement(element);
System.Diagnostics.Trace.TraceInformation("deserialize 2.{0} end", logCounter++);
m_objectList.Add(obj);
}
通过使用DebugViewer查看,发现上面加log之间的的代码占用了较长的时间,代码实现如下
public static TLoadFromXElement(XElement element)
{
int logCounter = 0;
T serializableObject = null;
Type ObjectType = typeof(T);
XmlRootAttribute rootAttr = newXmlRootAttribute();
rootAttr.ElementName =element.Name.ToString();
rootAttr.IsNullable = true;
System.Diagnostics.Trace.TraceInformation("LoadFromXElement Beforector {0}", logCounter++);
XmlSerializer xmlSerializer = newXmlSerializer(ObjectType, rootAttr);
System.Diagnostics.Trace.TraceInformation("LoadFromXElementAfter ctor {0}", logCounter++);
using (XmlReader xmlReader =element.CreateReader())
{
System.Diagnostics.Trace.TraceInformation("LoadFromXElement -before des {0}", logCounter++);
serializableObject =xmlSerializer.Deserialize(xmlReader) as T;
}
System.Diagnostics.Trace.TraceInformation("LoadFromXElement afterdes");
return serializableObject;
}
最终发现上面加log的两行代码占用了较多时间,通过查阅一些资料发现,XmlSerializer在实例化时会根据其构造函数中的Type参数产生临时的assemblies来进行序列化或者反序列化的工作,从来提高效率,但是如果构造函数选用的不当,就会有内存泄露和性能问题。当使用下面的构造函数时,.NET会重用临时产生的assemblies
XmlSerializer.XmlSerializer(Type)
XmlSerializer.XmlSerializer(Type,String)
但是,如果使用其他的构造函数则会生成同一个临时assembly的多个不同版本,即第一次调用XmlSerializer的构造函数时临时生产一个版本的assemblyA,第二次调用XmlSerializer的构造函数时会生成A的另一个版本,第三次调用XmlSerializer的构造函数时会生成A的第三个版本。。。而之前生成的临时版本还不会被卸载,从而就导致了内存泄露和性能问题
上面的代码使用了XmlSerializer(Type,XmlRootAttribute)构造函数,所以才导致了性能问题
详细说明参见XmlSerializer Class in MSDN,内容摘录如下:
Dynamically GeneratedAssemblies
Toincrease performance, the XML serialization infrastructure dynamically generatesassemblies to serialize and deserialize specified types. The infrastructurefinds and reuses those assemblies. This behavior occurs only when using thefollowing constructors:
XmlSerializer.XmlSerializer(Type)
XmlSerializer.XmlSerializer(Type, String)
Ifyou use any of the other constructors, multiple versions of the same assemblyare generated and never unloaded, which results in a memory leak and poorperformance. The easiest solution is to use one of the previously mentioned twoconstructors. Otherwise, you must cache the assemblies in a Hashtable
解决办法:
使用Dictionary将第一次实例化的XmlSerializer对象存起来,使用其构造函数的参数作为Key,以后再对同样的Type实例化XmlSerializer时直接从Dictionary中取,改进后的代码
private static readonlyDictionary<string, XmlSerializer> cacheSerializer = newDictionary<string, XmlSerializer>();
public static XmlSerializerGetSerializer(Type type, XmlRootAttribute rootAttr)
{
var key = string.Format("{0}:{1}",type, rootAttr.ElementName);
XmlSerializer serializer;
if(!cacheSerializer.TryGetValue(key, out serializer))
{
serializer = newXmlSerializer(type, rootAttr);
cacheSerializer.Add(key,serializer);
}
return serializer;
}
使用
XmlSerializerxmlSerializer = new XmlSerializer(ObjectType, rootAttr);
// 按照如下方式使用
XmlSerializerxmlSerializer = GetSerializer(ObjectType, rootAttr);
这样就大大提升了性能
参考链接:
http://www.damirscorner.com/XmlSerializerConstructorPerformanceIssues.aspx
http://blogs.msdn.com/b/crm/archive/2009/02/02/boost-performance-with-pre-generated-xmlserializers.aspx
Playing with the XmlSerializer
XmlSerializer Performance Issue when SpecifyingXmlRootAttribute