.NET 中 MD5 編碼的內存泄露問題分析
問題描述與定位
最近一個項目中要加工處理700多萬條的三元組數據,總是在執行到二三百萬條的時候就報內存溢出了。不斷的檢查代碼,各種對象局部化;使用.net profiler分析堆棧內存,發現有大量的String對象創建沒有及時回收,于是對程序中各處的字符串拼接做了優化處理,但是結果不是很明顯,還是會出現內存溢出的情況,只不過出現的晚一點。
又經過反復的對代碼段注釋測試,最后定位到可能出現內存泄露的函數(被調用700萬次以上)如下:
public static string MD5Encode(string source) { if (string.IsNullOrEmpty(source)) return source; MD5 md5 = new MD5CryptoServiceProvider(); byte[] s = md5.ComputeHash(Encoding.UTF8.GetBytes(source)); return BitConverter.ToString(s).Replace("-", ""); }
分別注釋8、7、6行代碼,發現只有md5對象的創建時候,還是會出現內存溢出。OK,最后確定造成內存泄露的對象就是MD5CryptoServiceProvider。
問題解決
找到問題的原因了,就開始嘗試解決辦法,既然MD5CryptoServiceProvider對象的創建會造成內存泄露,就只創建一個對象實例試試(單例化),修改后,代碼如下(代碼相對簡單,注釋已移除):
public static string MD5Encode(string source) { if (string.IsNullOrEmpty(source)) return source; MD5 md5 = GetMd5Instance(); byte[] s = md5.ComputeHash(Encoding.UTF8.GetBytes(source)); return BitConverter.ToString(s).Replace("-", ""); } private static MD5CryptoServiceProvider _md5Instance; private MD5CryptoServiceProvider GetMd5Instance() { return _md5Instance ?? (_md5Instance = new MD5CryptoServiceProvider()); }
經過幾輪測試,沒有再出現內存溢出,問題解決了。
原理依據
既然 MD5CryptoServiceProvider會造成內存泄露,肯定是要有原因的,微軟也給出了提示,這個類是非線程安全的。MSDN的描述如下:
使用建議
既然 MD5CryptoServiceProvider的實例是非線程安全的,使用單例模式也是一種辦法。同時,如果不考慮和老系統的兼容問題,請使用新的取hash的算法sha,MSDN上面也有建議:
SHA1、SHA384、SHA256、SHA512都有線程安全的子類:{X}Managed,可以使用這樣的子類放心創建實例:
public static string Md5Encode(string source) { if (string.IsNullOrEmpty(source)) return source; //MD5 md5 = GetMd5Instance(); //byte[] s = md5.ComputeHash(Encoding.UTF8.GetBytes(source)); SHA512 shaM = new SHA512Managed(); byte[] s = shaM.ComputeHash(Encoding.UTF8.GetBytes(source)); //SHA1 shaM = new SHA1Managed(); //byte[] s = shaM.ComputeHash(Encoding.UTF8.GetBytes(source)); return BitConverter.ToString(s).Replace("-", ""); }
當然, {X} CryptoServiceProvider類型的實例依然是非線程安全的,要是使用 {X} CryptoServiceProvider,仍然要注意內存泄露問題。
來自: http://my.oschina.net/u/1052456/blog/552942?fromerr=KuQNR4Wc