文档章节

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

caltrop
 caltrop
发布于 2015/12/29 17:21
字数 873
阅读 2287
收藏 46

问题描述与定位

        最近一个项目中要加工处理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。

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

© 著作权归作者所有

共有 人打赏支持
caltrop
粉丝 2
博文 8
码字总数 5018
作品 0
海淀
高级程序员
加载中

评论(14)

自主创新
自主创新
我人为不应该用.net
久永
久永
@runescape
标题误解太大,建议修改,否则有哗众取宠&标题党之嫌疑。支持者+10086~
caltrop
caltrop

引用来自“AkataMoKa”的评论

我看到你给的“原理依据”那里的MSDN截图,我怎么认为它说的是线程安全的?
是我英语不好吗?

静态成员线程安全,实例不保证线程安全
AkataMoKa
AkataMoKa
我看到你给的“原理依据”那里的MSDN截图,我怎么认为它说的是线程安全的?
是我英语不好吗?
洛阳码农
eechen还有三十秒到达战场,碾碎他们!
caltrop
caltrop

引用来自“BinSys”的评论

MD5CryptoServiceProvider 内部调用了WIN的非托管crypto api,又native调用,你又没using,当然会泄露。

@BinSys @Minho 谢谢各位认真的解读,以后更要注重重构这些非安全代码。
kstrain
kstrain
谢谢你的分享。一直没注意这种问题,一般都网络上抄例子。
Glitter
Glitter
using解千愁
蓝水晶飞机
蓝水晶飞机
没事,循环里面重复new 更多的实例哈哈,很酸爽。
BinSys
BinSys
MD5CryptoServiceProvider 内部调用了WIN的非托管crypto api,又native调用,你又没using,当然会泄露。
XCode Static Analysis 静态分析工具分析代码

Clang静态分析和Instruments来剖析代码有一些不同,Clang更致力于在编译的过程中通过自身的一套判断机制来找出代码中潜在的隐患。   在XCode 3.2之后的版本里,Clang已经被集成进来,Stati...

奋斗的青春年华
2016/09/09
50
0
Android 内存泄露优化处理

参考: Android应用内存泄露分析、改善经验总结 使用新版Android Studio检测内存泄露和性能 解决安卓CPU使用率过高问题 Android CPU使用过大的问题解决以及造成的原因 AndroidStudio CPU Mo...

天鬼
2017/11/06
0
0
内存泄漏是什么??

什么是内存泄漏(memory leak)? 指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并非指内情人q存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了...

望夜的星空
2017/11/01
0
0
使用 Eclipse Memory Analyzer 进行堆转储文件分析

简介: Eclipse Memory Analyzer(MAT)是著名的跨平台集成开发环境 Eclipse Galileo 版本的 33 个组成项目中之一,它是一个功能丰富的 JAVA 堆转储文件分析工具,可以帮助你发现内存漏洞和减...

红薯
2010/07/26
1K
0
使用Xcode和Instruments调试解决iOS内存泄露

虽然iOS 5.0版本之后加入了ARC机制,由于相互引用关系比较复杂时,内存泄露还是可能存在。所以了解原理很重要。 这里讲述在没有ARC的情况下,如何使用Instruments来查找程序中的内存泄露,以...

张志浩
2013/02/09
0
1

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Ubuntu18.04 显卡GF-940MX安装NVIDIA-390.77

解决办法: 下面就给大家一个正确的姿势在Ubuntu上安装Nvidia驱动: (a)首先去N卡官网下载自己显卡对应的驱动:www.geforce.cn/drivers (b)下载后好放在英文路径的目录下,怎么简单怎么来...

AI_SKI
今天
0
0
深夜胡思乱想

魔兽世界 最近魔兽世界出了新版本, 周末两天升到了满级,比之前的版本体验好很多,做任务不用抢怪了,不用组队打怪也是共享拾取的。技能简化了很多,哪个亮按哪个。 运维 服务器 产品 之间的...

Firxiao
今天
1
0
MySQL 8 在 Windows 下安装及使用

MySQL 8 带来了全新的体验,比如支持 NoSQL、JSON 等,拥有比 MySQL 5.7 两倍以上的性能提升。本文讲解如何在 Windows 下安装 MySQL 8,以及基本的 MySQL 用法。 下载 下载地址 https://dev....

waylau
今天
0
0
微信第三方平台 access_token is invalid or not latest

微信第三方开发平台code换session_key说的特别容易,但是我一使用就带来无穷无尽的烦恼,搞了一整天也无济于事. 现在记录一下解决问题的过程,方便后来人参考. 我遇到的这个问题搜索了整个网络也...

自由的开源
今天
3
0
openJDK之sun.misc.Unsafe类CAS底层实现

注:这篇文章参考了https://www.cnblogs.com/snowater/p/8303698.html 1.sun.misc.Unsafe中CAS方法 在sun.misc.Unsafe中CAS方法如下: compareAndSwapObject(java.lang.Object arg0, long a......

汉斯-冯-拉特
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部