2011-06-04 108 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; 
      } 
     } 

    } 

注:通常情況下,內存泄漏意味着應用程序會隨着時間慢慢消耗更多的和更多的內存(這是永遠不會回收)。這是你正在觀察的,還是你的內存消耗只是變得更高,雖然穩定當你改變實現?爲了真正確認你確實有內存泄漏,真正的原因是什麼(哪些對象不能被收集/定案),你通常必須使用內存分析器。