• XmlSerializer带来的性能问题及解决办法


    本文中的思路、二元hashtable等,大力感谢Leo Chen的帮助!
    对于XmlSerializer带来的内存占用过高,最终导致Out Of Memory的问题,参见以前这个链接:http://www.cnblogs.com/juqiang/archive/2008/01/15/1039936.html
    (但是那篇文章中对于XmlSerializer构造方法的说明,是错误的。那段代码没有问题,有问题的是下面的)

    首先看System.Xml.Serialization.XmlSerializer的构造方法,一共分为三大类:

    public XmlSerializer(Type type) : this(type, (string) null)
    public XmlSerializer(Type type, string defaultNamespace)
    这两个方法,采用了上文引用的那篇文章的处理方式,应用了cache,这是正确的,不会造成内存占用过高。(上文引用那篇文章里面这个地方解释错了)

    另一大类的方法是:
    public XmlSerializer(XmlTypeMapping xmlTypeMapping),这个方法里面会产生一个tempAssembly,但是没有用缓存方式来处理。

    有问题的一类方法是:
    public XmlSerializer(Type type, Type[] extraTypes) : this(type, null, extraTypes, null, null, null, null)
    public XmlSerializer(Type type, XmlAttributeOverrides overrides) : this(type, overrides, new Type[0], null, null, null, null)
    public XmlSerializer(Type type, XmlRootAttribute root) : this(type, null, new Type[0], root, null, null, null)
    public XmlSerializer(Type type, XmlAttributeOverrides overrides, Type[] extraTypes, XmlRootAttribute root, string defaultNamespace) : this(type, overrides, extraTypes, root, defaultNamespace, null, null)
    public XmlSerializer(Type type, XmlAttributeOverrides overrides, Type[] extraTypes, XmlRootAttribute root, string defaultNamespace, string location, Evidence evidence)

    前4个都最终调用了最后一个构造方法,里面产生了一个tempAssembly,也没有用缓存方式来护理。

    我上次提到的那个问题,问题代码如下:
    public static TReturn Convert<TReturn, TInput>(TInput input) where TReturn: class, new() where TInput: IProvisioningObject
    {
        using (MemoryStream stream = new MemoryStream())
        {
            new XmlSerializer(typeof(TInput)).Serialize((Stream) stream, input);
            stream.Position = 0L;
            XmlSerializer serializer = new XmlSerializer(typeof(TReturn), new XmlRootAttribute(input.GetType().Name));
            return (TReturn) serializer.Deserialize(stream);
        }
    }
    注意红色的代码,这里产生了一个tempAssembly,没有做缓存。这里的核心问题在于,tempAssembly不会被自动释放掉,除非appdomain被unload。
    修正的方式类似于XmlSerializer的处理方式,采用一个二元Hashtable来做。代码修改如下:
     1 private static TempXmlSerializerCache cache = new TempXmlSerializerCache();
     2
     3        public static TReturn Convert<TReturn, TInput>(TInput input)
     4            where TReturn : classnew()
     5            where TInput : IProvisioningObject
     6        {
     7            using (MemoryStream stream = new MemoryStream())
     8            {
     9                new XmlSerializer(typeof(TInput)).Serialize((Stream)stream, input);
    10                stream.Position = 0L;
    11
    12                XmlSerializer serializer = cache[typeof(TReturn).ToString(), input.GetType().Name];
    13                if (serializer == null)
    14                {
    15                    lock (cache)
    16                    {
    17                        serializer = cache[typeof(TReturn).ToString(), input.GetType().Name];
    18                        if (serializer == null)
    19                        {
    20                            serializer = new XmlSerializer(typeof(TReturn), new XmlRootAttribute(input.GetType().Name));
    21                            cache.Add(typeof(TReturn).ToString(), input.GetType().Name, serializer);
    22                        }

    23                    }

    24                }

    25                return (TReturn)serializer.Deserialize(stream);
    26            }

    27        }

    这里的二元Hashtable,是从XmlSerializer里面扒出来的,稍微修改了一下而已:

        public class TempXmlSerializerCache
        
    {
            
    private Hashtable cache = new Hashtable();

            
    public void Add(string ns, object o, XmlSerializer serializer)
            
    {
                XmlSerializerCacheKey key 
    = new XmlSerializerCacheKey(ns, o);
                
    lock (this)
                
    {
                    
    if (this.cache[key] != serializer)
                    
    {
                        Hashtable hashtable 
    = new Hashtable();
                        
    foreach (object obj2 in this.cache.Keys)
                        
    {
                            hashtable.Add(obj2, 
    this.cache[obj2]);
                        }

                        
    this.cache = hashtable;
                        
    this.cache[key] = serializer;
                    }

                }

            }


            
    public XmlSerializer this[string ns, object o]
            
    {
                
    get
                
    {
                    
    return (XmlSerializer)this.cache[new XmlSerializerCacheKey(ns, o)];
                }

            }

        }


    以及:
       public class XmlSerializerCacheKey
        
    {
            
    private string ns;
            
    private object type;

            
    public XmlSerializerCacheKey(string ns, object type)
            
    {
                
    this.type = type;
                
    this.ns = ns;
            }


            
    public override bool Equals(object o)
            
    {
                XmlSerializerCacheKey key 
    = o as XmlSerializerCacheKey;
                
    if (key == null)
                
    {
                    
    return false;
                }

                
    return ((key.type == this.type) && (key.ns == this.ns));
            }


            
    public override int GetHashCode()
            
    {
                
    return (((this.ns != null? this.ns.GetHashCode() : 0^ ((this.type != null? this.type.GetHashCode() : 0));
            }

        }


    经过修改、编译、发布,再次测试,问题得到了解决,哈哈!!!

    本文中的思路、二元hashtable等,大力感谢Leo Chen的帮助!
  • 相关阅读:
    Emmet使用
    正则参考网址
    sublime通用快捷键 汉化 安装 插件
    【真正福利】成为专业程序员路上用到的各种优秀资料、神器及框架
    生产事故的优化经历
    Windows下oracle打补丁步骤
    Oracle10g完全卸载正确步骤
    在windows2003系统上安装两个版本的oracle
    oracle11g数据库升级数据库升级
    oracle Imp和exp以及导入常见的错误
  • 原文地址:https://www.cnblogs.com/juqiang/p/1100984.html
Copyright © 2020-2023  润新知