2011-06-04 129 views
8

我最近采取了WCF Windows服务的所有权,使大量使用了下面的静态实用工具类的检索查找数据:这段代码为什么会造成内存泄漏?

public static class Utility 
    { 

     //begin code that causes increased memory consumption 
     private static Dictionary<string, ErrorData> _errorData; 

     internal static Dictionary<string, ErrorData> ErrorData 

     { 
      get 
      { 
       if (_errorData == null) 
       { 
        _errorData = GetErrorData(); 
       } 
       return _errorData; 
      } 

     } 
     //end code that causes increased memory consumption 

     /// GetErrorData method to get error messages from error xml 
     /// </summary>   
     /// <returns>Dictionary of Error messages value for different fields.</returns>   
     internal static Dictionary<string, ErrorData> GetErrorData() 
     { 
      Dictionary<string, ErrorData> data = null; 


       XmlDocument doc = LoadXmlDocument(Constants.ErrorMessagesFileName); 
       XmlNodeList errorNode = doc.SelectNodes("/ErrorMessages/Error"); 
       data = new Dictionary<string, ErrorData>(); 

       foreach (XmlNode node in errorNode) 
       { 
        ErrorData errorValues = new ErrorData(); 
        errorValues.FieldName = node.Attributes["FieldName"].Value; 
        errorValues.ErrorMessage = node.Attributes["ErrorMessage"].Value; 
        data.Add(node.Attributes["code"].Value, errorValues); 
       } 


      return data; 
     } 
     internal static XmlDocument LoadXmlDocument(string xmlFileName) 
     { 
      XmlDocument doc = null; 
      try 
      { 
       if (HttpRuntime.Cache[xmlFileName] == null) 
       { 
        doc = new XmlDocument(); 
        doc.Load(Constants.Folderpath + "\\" + xmlFileName); 
        HttpRuntime.Cache.Insert(xmlFileName, doc); 
       } 
       else 
       { 
        doc = (XmlDocument)HttpRuntime.Cache[xmlFileName]; 
       } 
      } 
      catch (Exception ex) 
      { 
       //log 
      } 
      return doc; 
     } 
    } 

正如你所看到的,静态errordata子属性使用了一个私人的后盾领域。 ErrorData是一个使用文件系统上的XML资源构建的字典,这就是为什么初次检索时文件的内容存储在HttpRuntime.Cache中的原因。

在正常负载下,该服务消耗大约120 MB的RAM。

在某些时候,一个团队成员认为需要通过创建一个由延迟加载的静态字段支持的静态属性来引入另一个级别的优化。无论如何,在几次调用服务之后,所述静态字段的存在会导致严重的内存泄漏(500MB +)。

当我删除静态字段和属性(客户端改为调用Utility.GetErrorData())时,内存消耗回到正常水平。

任何人都可以解释为什么这个静态字段的存在导致内存泄漏?如果这有所帮助,WCF服务将与InstanceContextMode.PerCall一起运行。

非常感谢。

+0

你怎么知道这是造成内存泄漏?它是不是只是使用大量的内存,并保持它静止?什么是您的服务的实例?它是PerCall吗?辛格尔顿?会议? – 2011-06-04 18:52:40

+1

它实际上是内存泄漏还是GC运行不够,或者认为它不需要释放内存。 – 2011-06-06 01:23:59

+1

在DEBUG模式或RELEASE模式下运行?在RELEASE模式不会的情况下,DEBUG模式会泄漏。 – Felan 2011-06-08 20:26:34

回答

0

我不完全确定当你谈论这个改变时你的代码有什么变化。然而,从阅读代码我的猜测是,你最终不止一次地调用GetErrorData,并且字典只是填满了大量重复的条目。如果添加日志代码,哪些代码显示为重复输入?

马丁

1

如果错误的文件非常大,那么静态版本加载巨大的XML文档到内存中,并永远不会释放它。以前,如果客户端调用GetErrorData(),那么数据将被加载到内存中并返回,清理内存。

这里没有同步,所以如果没有加载静态变量,几个同时发生的请求将开始单独加载错误文档。只有一个字典会赢得并保存到静态变量。但是,如果错误文件很大,则多个线程同时加载会增加内存压力。如果是这样的话,我预计下一次垃圾回收会回收额外的实例并释放大部分内存。

另请注意,静态实例版本一次加载错误文件。所以如果创建了额外的错误,这些错误永远不会返回给客户端。

+0

是的,因为没有同步,所以多次竞争GetErrorData调用最终会将所有实例添加到Http缓存中,因此可能会将多个相同的XML数据副本存储在内存中。 – Mahol25 2011-08-24 12:18:00

0

是否使添加同步修复您的“内存泄漏”?

也就是说,例如使LoadXmlDocument()和GetErrorData()私人和修改errordata子属性是这样的

private static Dictionary<string, ErrorData> _errorData; 
    private static object lockObject = new object(); 

    internal static Dictionary<string, ErrorData> ErrorData 
    { 
     get 
     { 
      lock (lockObject) 
      { 
       if (_errorData == null) 
       { 
        _errorData = GetErrorData(); 
       } 
       return _errorData; 
      } 
     } 

    } 

注:通常情况下,内存泄漏意味着应用程序会随着时间慢慢消耗更多的和更多的内存(这是永远不会回收)。这是你正在观察的,还是你的内存消耗只是变得更高,虽然稳定当你改变实现?为了真正确认你确实有内存泄漏,真正的原因是什么(哪些对象不能被收集/定案),你通常必须使用内存分析器。