.NET中MD5编码的内存泄露问题分析

in 编程
关注公众号【好便宜】( ID:haopianyi222 ),领红包啦~
阿里云,国内最大的云服务商,注册就送数千元优惠券:https://t.cn/AiQe5A0g
腾讯云,良心云,价格优惠: https://t.cn/AieHwwKl
搬瓦工,CN2 GIA 优质线路,搭梯子、海外建站推荐: https://t.cn/AieHwfX9

问题描述与定位

        最近一个项目中要加工处理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,仍然要注意内存泄露问题。

解决方案补充

       首先感谢网友们积极认真的回复解答,你们让我受益匪浅。

        上面我的分析和给出的解决方案都表面化了,非常抱歉。从朋友们的回复中可以看到,问题的根本原因是对.net中的非托管对象理解不深,使用对象错误造成内存泄露。对于非托管代码,c#提供了Dispose模式,平时只关注流相关的对象,很少关注其他对象,以后多加注意,加深理解。

        以免内存泄露,正确使用MD5CryptoServiceProvider对象,应该像使用流对象(非托管)一样,在fin里调用Dispose,或者使用using包围,让编译器帮忙调用Dispose。

再次谢谢大家帮忙解决问题;有其他方案的,欢迎继续回复!

关注公众号【好便宜】( ID:haopianyi222 ),领红包啦~
阿里云,国内最大的云服务商,注册就送数千元优惠券:https://t.cn/AiQe5A0g
腾讯云,良心云,价格优惠: https://t.cn/AieHwwKl
搬瓦工,CN2 GIA 优质线路,搭梯子、海外建站推荐: https://t.cn/AieHwfX9
扫一扫关注公众号添加购物返利助手,领红包
Comments are closed.

推荐使用阿里云服务器

超多优惠券

服务器最低一折,一年不到100!

朕已阅去看看