减少.NET应用程序内存占用的一则实践(2)_.Net教程
推荐:进程性能计数器已禁用的解决办法未处理的异常:进程性能计数器已禁用 调试出错: 未处理的异常: System.InvalidOperationException: 进程性能计数器已禁用,因此无法执行所请求的操作 问题解决: 方法一: 这时只要修复一下windows的性能计数器即可。 具体方法:在运行中输入:lodctr /r 然后回车,运
该同学指出.NET应用程序和其他使用本地代码编写的程序相比会有较大的内存占用,如果对内存开销比较在意,.NET可能不是最好的选择。.NET 应用程序的内存一定程度上受垃圾回收的影响。并指出,一些数据结构如List,系统会分配多余的空间。可以使用值类型而不是引用类型,不要创建大对象,以免产生内存碎片等等降低内存占用的建议。
这些都考虑过之后,内存还是达不到要求,所以开始寻找调用非托管代码的方式来自己更灵活的控制内存的分配与销毁。但是整个程序都是采用.NET编写的,全部切换成C或者C++不现实,所以只有两种方案,一是使用unsafe 代码,二是将数据加载和检索模块采用C或者C++编写,在.NET中采用P/Invoke技术调用。
刚开始想采用unsafe代码,对数据的加载及检索直接在放在unsafe 代码中。后来觉得代码有些乱,不同风格的代码混杂在一起不太好,而且数据加载和检索的逻辑也比较复杂。所以就直接采用第二种方案,使用C++编写数据加载和检索逻辑。然后在.NET里面调用。
在开始之前,也做了一些评估,比如将同样的10M的数据加载到内存中,都采用字符串的方式存储,.NET中会占用20-30M的内存,而在C++中只有9-10M的样子,而且变动很小。这正是需要的结果。
由于对C++不熟,临时抱佛脚,翻了下C++ Primier Plus中关于字符串和STL的相关章节,并请求其他开发小组给予了一定的协助,定义了基本的接口。为了演示,我创建了两个工程,一个是名为 SecuData的C++ Win32 DLL工程,一个是测试该类库的名为SecuDataTest的C# WinForm程序。
我在C++中定义好了4个方法,一个初始化加载数据,一个设置搜索优先级,一个查找匹配方法和一个卸载数据方法,具体的算法由于工作原因不便贴出,这里只是举一个简单的例子,方法名及工程结构如下图:
然后再在.NET中使用P/Invoke技术引入C++ DLL中定义的方法。
这样就可以在.NET中调用这些方法了,需要说明的是,方法的传入值这里是使用String类型的,第二个StringBuilder类型的参数是方法的真正返回值,方法的整体int型返回值表明方法是否执行成功。在调用查找方法时,第二个StringBuilder参数必须初始化一个最大的查询结果的大小,因为在C++中会往这个对象中写入结果,不初始化或者初始化太小都会抛出异常。当然也可以直接返回结构体,这个就需要额外定义,这里返回的都是字符串。完了自己在.NET里面对其进行解析。
需要注意的是,调试的时候,如果需要调试C++里面的代码,需要指定DLL的生成目录及启动目标,并且将C++项目设置为启动项目,这里我设定生成 DLL的目录为SecuDataTest项目生成的SecuDataTest.exe文件所在的目录,调试启动目标设置为 SecuDataTest.exe,这样在C++项目中设置断点,启动.NET Winform程序,当P/Invoke触发断点时能够逐步调试C++代码。
在发布的时候,最好将默认的动态库配置修改为静态库,这样VS会把依赖的相关C++库打包到生成的dll中,部署到客户机器上不会出现问题。SecuData类库项目的属性设置如下图:
改成这种P/Invoke模式之后,10M数据载到内存中,内存占用只有10M左右,较之前采用.NET的30-40M的内存又降低了很多,而且内存波动比较小,满足了对内存占用的要求。
采用这种“混搭”方式有一些好处,既有.NET的快速开发,又有C++的灵活的内存分配销毁模式以及代码安全性保护。在很多时候,可以将一些对内存占用比较敏感,大数据量的处理逻辑,放在C++中处理,利用灵活的手动内存管理模式降低这部分的内存占用;将核心的数据结构及算法使用C++编写,可以提高代码的安全性,提高程序的反编译难度。
四、结语
.NET应用程序由于需要加载CLR及一些通用类库,并且具有垃圾收集机制,较其他本地语言如C,C++具有较大的footprint,使用.NET创建一个简单的Winform可能就会占用近10M的内存,所以随着开发的进行,内存占用会比较大。当然这些在很多时候是由于开发者自身对.NET底层机制不熟悉,比如在有些地方可以使用值类型而使用引用类型;创建了大量的临时的周期比较短的对象;使用了过多的静态变量及成员导致内存被长久占用而得不到回收;以及.NET内部的一些机制,比如集合对象会在内部预先分配多余的空间等等。很多时候因为有.NET的GC机制,使得我们不必去关注对象的销毁而很”大方”的去创建新对象,去使用一些重型的内置对象,从而导致内存占用过大。解决好这些问题,其实可以降低.NET应用程序的相当大一部分的不必要的内存占用。
除了了解.NET框架的一些内部机制之外,良好的思路,高效数据结构和算法也可以使得问题变得简单而减少内存的开销。
最后对于对内存要求比较敏感,可以利用C/C++的手动的灵活的内存管理语言来编写相应模块,在.NET中采用P/Invoke技术进行调用来减少一些内存。
以上是我对降低.NET应用程序内存占用的一点儿实践和总结,希望对您有所帮助。
分享:ASP.NET获取MS SQL Server安装实例View Code protected void Page_Load(object sender, EventArgs e) { DataTable dataTable = SqlDataSourceEnumerator.Instance.GetDataSources(); foreach (DataRow dr in dataTable.Rows) { if (string.IsNullOrEmpty(dr[InstanceName].ToString())) this.DropDownLi
- asp.net如何得到GRIDVIEW中某行某列值的方法
- .net SMTP发送Email实例(可带附件)
- js实现广告漂浮效果的小例子
- asp.net Repeater 数据绑定的具体实现
- Asp.Net 无刷新文件上传并显示进度条的实现方法及思路
- Asp.net获取客户端IP常见代码存在的伪造IP问题探讨
- VS2010 水晶报表的使用方法
- ASP.NET中操作SQL数据库(连接字符串的配置及获取)
- asp.net页面传值测试实例代码
- DataGridView - DataGridViewCheckBoxCell的使用介绍
- asp.net中javascript的引用(直接引入和间接引入)
- 三层+存储过程实现分页示例代码
- 相关链接:
- 教程说明:
.Net教程-减少.NET应用程序内存占用的一则实践(2)。