Wednesday, January 16, 2013

XmlSerializer class may result in a memory leak and poor performance

We have recently found an unpleasant memory leak related with XmlSerializer class. This issue is described in MSDN:  
To increase performance, the XML serialization infrastructure dynamically generates assemblies to serialize and deserialize specified types. The infrastructure finds and reuses those assemblies. This behavior occurs only when using the following constructors: 
If you use any of the other constructors, multiple versions of the same assembly are generated and never unloaded, which results in a memory leak and poor performance. The easiest solution is to use one of the previously mentioned two constructors. Otherwise, you must cache the assemblies.
So be careful with XmlSerializer class.
I have created a factory class which creates XmlSerializer instances and avoids this issue.

  1. public static class CachingXmlSerializerFactory
  2.     {
  3.         private static readonly Dictionary<string, XmlSerializer> Cache = new Dictionary<string, XmlSerializer>();
  4.  
  5.         private static readonly object SyncRoot = new object();
  6.  
  7.         public static XmlSerializer Create(Type type, XmlRootAttribute root)
  8.         {
  9.             if (type == null) throw new ArgumentNullException("type");
  10.             if (root == null) throw new ArgumentNullException("root");
  11.  
  12.             var key = String.Format(CultureInfo.InvariantCulture, "{0}:{1}", type, root.ElementName);
  13.  
  14.             lock (SyncRoot)
  15.             {
  16.                 if (!Cache.ContainsKey(key))
  17.                 {
  18.                     Cache.Add(key, new XmlSerializer(type, root));
  19.                 }
  20.             }
  21.  
  22.             return Cache[key];
  23.         }
  24.  
  25.         public static XmlSerializer Create<T>(XmlRootAttribute root)
  26.         {
  27.             return Create(typeof(T), root);
  28.         }
  29.  
  30.         public static XmlSerializer Create<T>()
  31.         {
  32.             return Create(typeof(T));
  33.         }
  34.  
  35.         public static XmlSerializer Create<T>(string defaultNamespace)
  36.         {
  37.             return Create(typeof(T), defaultNamespace);
  38.         }
  39.  
  40.         public static XmlSerializer Create(Type type)
  41.         {
  42.             return new XmlSerializer(type);
  43.         }
  44.  
  45.         public static XmlSerializer Create(Type type, string defaultNamespace)
  46.         {
  47.             return new XmlSerializer(type, defaultNamespace);
  48.         }
  49.     }

2 comments:

  1. Awesome, and fixed a day's worth of struggling. Thanks!

    ReplyDelete
  2. Thanks for this. This is the first blog that truly addresses the problem and provides a solution .. all the others only partially address it.

    Just needed to add an example of how to call it:

    var xmlSerializer = CachingXmlSerializerFactory.Create(xml.GetType(), defnamespace);

    ReplyDelete