減少.NET應(yīng)用程序內(nèi)存占用的一則實踐(2)_.Net教程
推薦:進程性能計數(shù)器已禁用的解決辦法未處理的異常:進程性能計數(shù)器已禁用 調(diào)試出錯: 未處理的異常: System.InvalidOperationException: 進程性能計數(shù)器已禁用,因此無法執(zhí)行所請求的操作 問題解決: 方法一: 這時只要修復(fù)一下windows的性能計數(shù)器即可。 具體方法:在運行中輸入:lodctr /r 然后回車,運
該同學(xué)指出.NET應(yīng)用程序和其他使用本地代碼編寫的程序相比會有較大的內(nèi)存占用,如果對內(nèi)存開銷比較在意,.NET可能不是最好的選擇。.NET 應(yīng)用程序的內(nèi)存一定程度上受垃圾回收的影響。并指出,一些數(shù)據(jù)結(jié)構(gòu)如List,系統(tǒng)會分配多余的空間�?梢允褂弥殿愋投皇且妙愋�,不要創(chuàng)建大對象,以免產(chǎn)生內(nèi)存碎片等等降低內(nèi)存占用的建議。
這些都考慮過之后,內(nèi)存還是達不到要求,所以開始尋找調(diào)用非托管代碼的方式來自己更靈活的控制內(nèi)存的分配與銷毀。但是整個程序都是采用.NET編寫的,全部切換成C或者C++不現(xiàn)實,所以只有兩種方案,一是使用unsafe 代碼,二是將數(shù)據(jù)加載和檢索模塊采用C或者C++編寫,在.NET中采用P/Invoke技術(shù)調(diào)用。
剛開始想采用unsafe代碼,對數(shù)據(jù)的加載及檢索直接在放在unsafe 代碼中。后來覺得代碼有些亂,不同風(fēng)格的代碼混雜在一起不太好,而且數(shù)據(jù)加載和檢索的邏輯也比較復(fù)雜。所以就直接采用第二種方案,使用C++編寫數(shù)據(jù)加載和檢索邏輯。然后在.NET里面調(diào)用。
在開始之前,也做了一些評估,比如將同樣的10M的數(shù)據(jù)加載到內(nèi)存中,都采用字符串的方式存儲,.NET中會占用20-30M的內(nèi)存,而在C++中只有9-10M的樣子,而且變動很小。這正是需要的結(jié)果。
由于對C++不熟,臨時抱佛腳,翻了下C++ Primier Plus中關(guān)于字符串和STL的相關(guān)章節(jié),并請求其他開發(fā)小組給予了一定的協(xié)助,定義了基本的接口。為了演示,我創(chuàng)建了兩個工程,一個是名為 SecuData的C++ Win32 DLL工程,一個是測試該類庫的名為SecuDataTest的C# WinForm程序。
我在C++中定義好了4個方法,一個初始化加載數(shù)據(jù),一個設(shè)置搜索優(yōu)先級,一個查找匹配方法和一個卸載數(shù)據(jù)方法,具體的算法由于工作原因不便貼出,這里只是舉一個簡單的例子,方法名及工程結(jié)構(gòu)如下圖:

然后再在.NET中使用P/Invoke技術(shù)引入C++ DLL中定義的方法。

這樣就可以在.NET中調(diào)用這些方法了,需要說明的是,方法的傳入值這里是使用String類型的,第二個StringBuilder類型的參數(shù)是方法的真正返回值,方法的整體int型返回值表明方法是否執(zhí)行成功。在調(diào)用查找方法時,第二個StringBuilder參數(shù)必須初始化一個最大的查詢結(jié)果的大小,因為在C++中會往這個對象中寫入結(jié)果,不初始化或者初始化太小都會拋出異常。當然也可以直接返回結(jié)構(gòu)體,這個就需要額外定義,這里返回的都是字符串。完了自己在.NET里面對其進行解析。
需要注意的是,調(diào)試的時候,如果需要調(diào)試C++里面的代碼,需要指定DLL的生成目錄及啟動目標,并且將C++項目設(shè)置為啟動項目,這里我設(shè)定生成 DLL的目錄為SecuDataTest項目生成的SecuDataTest.exe文件所在的目錄,調(diào)試啟動目標設(shè)置為 SecuDataTest.exe,這樣在C++項目中設(shè)置斷點,啟動.NET Winform程序,當P/Invoke觸發(fā)斷點時能夠逐步調(diào)試C++代碼。
在發(fā)布的時候,最好將默認的動態(tài)庫配置修改為靜態(tài)庫,這樣VS會把依賴的相關(guān)C++庫打包到生成的dll中,部署到客戶機器上不會出現(xiàn)問題。SecuData類庫項目的屬性設(shè)置如下圖:

改成這種P/Invoke模式之后,10M數(shù)據(jù)載到內(nèi)存中,內(nèi)存占用只有10M左右,較之前采用.NET的30-40M的內(nèi)存又降低了很多,而且內(nèi)存波動比較小,滿足了對內(nèi)存占用的要求。
采用這種“混搭”方式有一些好處,既有.NET的快速開發(fā),又有C++的靈活的內(nèi)存分配銷毀模式以及代碼安全性保護。在很多時候,可以將一些對內(nèi)存占用比較敏感,大數(shù)據(jù)量的處理邏輯,放在C++中處理,利用靈活的手動內(nèi)存管理模式降低這部分的內(nèi)存占用;將核心的數(shù)據(jù)結(jié)構(gòu)及算法使用C++編寫,可以提高代碼的安全性,提高程序的反編譯難度。
四、結(jié)語
.NET應(yīng)用程序由于需要加載CLR及一些通用類庫,并且具有垃圾收集機制,較其他本地語言如C,C++具有較大的footprint,使用.NET創(chuàng)建一個簡單的Winform可能就會占用近10M的內(nèi)存,所以隨著開發(fā)的進行,內(nèi)存占用會比較大。當然這些在很多時候是由于開發(fā)者自身對.NET底層機制不熟悉,比如在有些地方可以使用值類型而使用引用類型;創(chuàng)建了大量的臨時的周期比較短的對象;使用了過多的靜態(tài)變量及成員導(dǎo)致內(nèi)存被長久占用而得不到回收;以及.NET內(nèi)部的一些機制,比如集合對象會在內(nèi)部預(yù)先分配多余的空間等等。很多時候因為有.NET的GC機制,使得我們不必去關(guān)注對象的銷毀而很”大方”的去創(chuàng)建新對象,去使用一些重型的內(nèi)置對象,從而導(dǎo)致內(nèi)存占用過大。解決好這些問題,其實可以降低.NET應(yīng)用程序的相當大一部分的不必要的內(nèi)存占用。
除了了解.NET框架的一些內(nèi)部機制之外,良好的思路,高效數(shù)據(jù)結(jié)構(gòu)和算法也可以使得問題變得簡單而減少內(nèi)存的開銷。
最后對于對內(nèi)存要求比較敏感,可以利用C/C++的手動的靈活的內(nèi)存管理語言來編寫相應(yīng)模塊,在.NET中采用P/Invoke技術(shù)進行調(diào)用來減少一些內(nèi)存。
以上是我對降低.NET應(yīng)用程序內(nèi)存占用的一點兒實踐和總結(jié),希望對您有所幫助。
分享: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發(fā)送Email實例(可帶附件)
- js實現(xiàn)廣告漂浮效果的小例子
- asp.net Repeater 數(shù)據(jù)綁定的具體實現(xiàn)
- Asp.Net 無刷新文件上傳并顯示進度條的實現(xiàn)方法及思路
- Asp.net獲取客戶端IP常見代碼存在的偽造IP問題探討
- VS2010 水晶報表的使用方法
- ASP.NET中操作SQL數(shù)據(jù)庫(連接字符串的配置及獲取)
- asp.net頁面?zhèn)髦禍y試實例代碼
- DataGridView - DataGridViewCheckBoxCell的使用介紹
- asp.net中javascript的引用(直接引入和間接引入)
- 三層+存儲過程實現(xiàn)分頁示例代碼
- 相關(guān)鏈接:
- 教程說明:
.Net教程-減少.NET應(yīng)用程序內(nèi)存占用的一則實踐(2)
。