當然啦,很多人開始學習C#的時候,就已經聽到過來自多方的警告,連接字符串的時候一定要用StringBuilder,不要使用String直接連接的方式,而且也都知道其中的原因,例如什麼因爲String是一個固定的變量,不能更改,每一次String連接的操作實際上都是創建了一個新的String實例。可能很少有人知道具體的數據是什麼,因爲我們不能盡信書本上說的,一定要有一些實驗數據纔可以。
讓我們看下面的兩份代碼,一份是使用String來進行字符串連接操作的:
using System;
public class StringTest { public static void Main() { string str = "";
DateTime begin = DateTime.Now; for (int i = 0; i < 100000; ++i) str += i; DateTime end = DateTime.Now;
Console.WriteLine(begin - end); } } |
另外一份是使用StringBuilder來進行字符串連接操作的。
using System; using System.Text;
public class StringBuilderTest { public static void Main() { StringBuilder str = new StringBuilder();
DateTime begin = DateTime.Now; for (int i = 0; i < 100000; ++i) str.Append(i); DateTime end = DateTime.Now;
Console.WriteLine(end - begin); } } |
當然啦,結果是大家都已經知道的,就是String版本的程序的速度要比StringBuilder的速度慢很多。
爲什麼String版本的程序會比StringBuilder程序慢那麼多呢—已經不是簡簡單單的倍數級別的差別了。這裏我介紹一個工具—CLR Profiler,程序員可以使用這個工具瞭解到自己的程序到底創建了多少個對象,GC發生了多少次,以及各個函數之間的調用關係是怎樣的。
1. 下載CLR Profiler以後,啓動它,點擊“Start Application”按鈕。
2. 選擇我們要剖析的.NET程序,實際上CLR Profiler還可以剖析我們的ASP.NET網站程序。
3. 等待程序執行完畢,CLR Profiler就會詳細地給你列出整個程序運行過程當中發生的問題了。
例如下圖(這個圖比上面的示例程序的循環次數少,只有原來的十分之一,因爲示例程序生成的日誌文件太大,我機器的內存被消耗光了),我們可以看到系統分配了379,458,639也就是將近400M的內存,而最後Final Heap內存的大小隻有355,527—就是最後我們的連接起來的最終字符串的大小,而整個循環過程當中,有340 + 19次的GC操作,我們知道GC操作是非常耗費時間的一個操作,這也就是爲什麼我們看到String版本的程序運行速度如此之慢。
點擊上圖裏面的“Allocation Graph”之後,我們會發現所有的內存分配操作都是爲System.String執行的,在內存當中,String的實例有20231個,佔去被分配內存空間的99.96%。