本文完全獨立於前兩篇文章。如果你喜歡它們,我希望你也會喜歡這個。在上一篇文章中,我展示了哪種方法更快,並比較了程式碼的執行速度。在本文中,我將展示不同程式碼片段的記憶體消耗情況。為了顯示記憶體對映和分配圖,我使用了CLR profiler 32位版本,和往常一樣,我在Windows平臺上使用了4GB RAM和Core i3 CPU。記憶體消耗或分配圖可能根據系統執行的程式而變化。因此,如果你得到一個不同的輸出或行為的程式碼,那麼請與我們分享你的經驗。
讓我們開始“改進c#程式碼的5個技巧:第3部分”的旅程。
StringBuilder消耗的記憶體比String少
在我的上一篇文章中,我已經展示了在長連線操作的場景中字串的速度有多慢。這裡我們會看到一個字串和StringBuilder的記憶體分配圖。讓我來演示一下。下面是我使用字串和StringBuilder進行相同操作的程式碼。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; using System.IO; using System.Net; using System.Net.NetworkInformation; using System.Threading; using System.Globalization; using System.Data.SqlClient; namespace Test1 { public class Test1 { string Name ; public void Process() { Name = Name + "A"; } } public class Test2 { StringBuilder sb = new StringBuilder(); public void Process() { sb.Append("A"); } } class Program { static void Main(string[] args) { Test1 t = new Test1(); t.Process(); Test2 t1 = new Test2(); t1.Process(); } } }
這是程式碼執行時的記憶體分配圖。
這裡我們從main函式呼叫兩個函式Process();儘管它們都有相同的名稱,但它們屬於不同的類和Test1.Process處理字串資料,而Test2.Process()處理StringBuilder資料。在分配圖中,我們可以看到字串處理函式消耗了Main()函式94%的資源,而Test2類中處理StringBuilder的Process()只消耗了Main()函式的0.21%的資源。
因此,結論是“當你想多次連線字串時,總是使用StringBuilder”。
如果可能的話,使用靜態函式
是的,如果可能的話,嘗試實現一個靜態函式,因為靜態物件(函式和資料)不屬於特定類的任何物件。這是大家都有的。因此,如果不建立物件,就不存在記憶體消耗的問題。下面我將展示一個靜態函式和靜態類的示例。看一下IL程式碼。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; using System.IO; using System.Net; using System.Net.NetworkInformation; using System.Threading; using System.Globalization; using System.Data.SqlClient; namespace Test1 { public static class mySclass { public static void Print() { Console.Write("Hello"); } } public class myNclass { public static void Print() { Console.Write("Hello"); } } class Program { static void Main(string[] args) { for (int i = 0; i < 1000; i++) { mySclass.Print(); myNclass.Print(); } } } }
IL程式碼在左手邊,在右手邊是由CLR分析器獲取的記憶體消耗類。由於空間消耗,我無法顯示CLR分析器的完整截圖。但是相信我,靜態類或函式沒有記憶體分配。
因此,結論是“如果可能,嘗試建立一個靜態函式並使用類名呼叫,而不是通過物件名呼叫通用函式”。
字串格式化VS字串連線
在第一點中,我展示了字串如何比StringBuilder消耗更多的資源。在這一點上,我將比較格式化輸出和字串連線。在第一個函式中,我使用了一個格式規範來列印格式化的輸出(基本上是連線一個字串)。在另一個函式中,我使用(+)操作符連線一個字串,如下所示:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; using System.IO; using System.Net; using System.Net.NetworkInformation; using System.Threading; using System.Globalization; using System.Data.SqlClient; namespace Test1 { class Test { public void Format() { int a = 100; Console.WriteLine("{0}AND{1}", a, a); } public void Concatination() { int a = 100; Console.WriteLine(a + "AND" +a ); } } class Program { static void Main(string[] args) { Test t = new Test(); t.Format(); t.Concatination(); Console.ReadLine(); } } }
在記憶體分配中我們會看到:
使用format列印字串的函式消耗了57%的資源,而簡單連線兩個字串的函式消耗了主函式的30%的資源。因此,我們可以清楚地看到,如果使用字串連線而不是輸出格式化,就可以節省系統資源。
空程式中哪個類消耗的資源最多?
首先,這一點並不推薦任何最佳實踐技術。我只是想說明,如果我們執行一個空程式(其中只有一個Main()函式),那麼分配了多少記憶體?下面是我的一個非常簡單的程式。
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Test1 { class Program { static void Main(string[] args) { } } }
是的,我沒有在這個程式裡寫任何東西。讓我們看看記憶體對映。
在這裡,我展示了與執行空程式場景相關的六個最耗費資源的類。可以清楚地看到,String類佔用了最多的資源(佔全部資源的25%)。現在提問。在一個我們從不使用字串的程式中,為什麼字串類消耗的資源最多?如果我們看一下這個程式的呼叫圖,我們會看到在main函式中有許多內部函式被呼叫,它們中的大多數都以字串作為引數,為了生成這些引數,CLR通常使用string類。如果你有不同的意見,請使用下面的評論框。
實現一個using塊來管理記憶體
始終實現一個using塊來管理資源是一個最佳實踐。實際上,我們可以證明使用block語句比不使用block語句消耗的記憶體更少。我們知道,如果我們實現一個using塊的塊程式碼大小可能會更大,因為using塊在內部在IL程式碼中建立了一個try catch,但一旦它在執行時在IL程式碼中實現,它就能有效地處理系統記憶體。為了演示這一點,我編寫了一個簡單的程式,如下所示:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; using System.IO; using System.Net; using System.Net.NetworkInformation; using System.Threading; using System.Globalization; using System.Data.SqlClient; namespace Test1 { class Test { public void Test1() { StreamWriter wr = new StreamWriter(@"D:\text.txt"); } public void Test2() { using (StreamWriter wr = new StreamWriter(@"D:\abc.txt")) { } } } class Program { static void Main(string[] args) { Test t = new Test(); t.Test1(); t.Test2(); } } }
在輸出部分,我組合了三個輸出螢幕。
在分配圖中,我們看到using塊比沒有using塊時消耗的資源更少,因為如果我們實現了using塊,程式可以有效地管理記憶體。
歡迎關注我的公眾號,如果你有喜歡的外文技術文章,可以通過公眾號留言推薦給我。
原文連結:https://www.c-sharpcorner.com/UploadFile/dacca2/5-tips-to-improve-your-C-Sharp-code-part-2/