C#字元串優化學習總結 記憶體區域 我們知道一個由C/C++編譯的程式占用的記憶體分為以下幾個部分: 1、棧區(stack): 由編譯器自動分配釋放 ,存放函數的參數值,局部變數的值等。其操作方式類似於數據結構中的棧。 2、堆區(heap) : 一般由程式員分配釋放, 若程式員不釋放,程式結束時可能由O ...
C#字元串優化學習總結
記憶體區域
我們知道一個由C/C++編譯的程式占用的記憶體分為以下幾個部分:
1、棧區(stack): 由編譯器自動分配釋放 ,存放函數的參數值,局部變數的值等。其操作方式類似於數據結構中的棧。
2、堆區(heap) : 一般由程式員分配釋放, 若程式員不釋放,程式結束時可能由OS回收 。註意它與數據結構中的堆是兩回事,分配方式倒是類似於鏈表。
3、全局區(靜態區)(static):全局變數和靜態變數的存儲都是在一塊的,初始化的全局變數和靜態變數在一塊區域, 未初始化的全局變數和未初始化的靜態變數在相鄰的另一塊區域, 程式結束後有系統釋放 。
4、常量區:常量、字元串常量池就是放在這裡的, 程式結束後由系統釋放 。
5、程式代碼區:存放函數體的二進位代碼。
C#常量池
C#也有自己的常量池,也就是我們所稱的暫存池(string intern pool),C#的字元串常量池不在堆中也不在棧中,是獨立的記憶體空間管理,在記憶體的常量區,由CLR(Common Language Runtime)維護這段記憶體。
其中,我們定義的例如string a = "HelloWorld";
,"HelloWorld"
這個我們定義的字面量就存儲在常量區中。如果再定義一個string b = "HelloWorld"
,這時候CLR就會去字元串常量池中找,如果存在相同內容的字元串對象的引用,則將這個引用返回。否則新的字元串對象被創建,然後將這個引用放入字元串常量池,並返回該引用。
關於常量池的理解:
-
常量池由CLR來維護,其中的所有字元串對象的值都不相同。
-
只有編譯階段的文本字元常量會被自動添加到常量池。
-
運行時期動態創建的字元串不會被加入到常量池中。
-
string.Intern()
可以把動態創建的字元串加入到常量池中。
即使這個動態創建的字元串和常量池中的某個字元串的值相等,引用也不會相等。
即使是動態創建的兩個字元串的值相等,他們的引用依然不相等。(charArray.ToString()特例)
字元串記憶體優化的核心原則有三個:
1、復用字元串,減少字元串數量
2、降低不可復用字元串的占用的記憶體
3、降低運行時產生的GC字元串記憶體
關於string
拼接和StringBuilder
拼接
1、在處理字元串時:string
是只可讀不可寫的,在進行字元串拼接時,往往是創建一個string
對象,然後棧中的記憶體指向堆中的新記憶體,在創建對象時需要分配記憶體空間,之前的記憶體則會產生GC。而StringBuilder
是存在於System.Text
命名空間下的在原來的記憶體中修改,不需要分配記憶體空間。
2、從記憶體優化方面來說,雖然StringBuilder
在拼接後仍需要調用ToString()
將拼接後的內容轉換成不可寫的字元串,但是相比較下來,頻繁的字元串操作StringBuilder
更好。
3、從功能上來說string
仍然比StringBuilder
更強。
4、string
主要用於公共API,通用性好,讀取性能高,占用記憶體小。
5、StringBuilder
主要用於拼接string
,修改性能好。
6、string
是不可變的,所以天然線程同步。
7、StringBuilder
可變,非線程同步。
如果是處理字元串的話,用string
中的方法每次都需要創建一個新的字元串對象並且分配新的記憶體地址,而StringBuilder
是在原來的記憶體里對字元串進行修改,所以在字元串處理方面還是建議用StringBuilder
這樣比較節約記憶體。但是string
類的方法和功能仍然還是比StringBuilder
類要強。
關於string+int
string
之所以可以與int
相加,根本上是調用了Concat
方法。首先int
轉object
需要裝箱,然後Concat
內部調用了所有object
的ToString
方法,然後再new一個字元串返回。而Concat
方法其實接受的是object
類型的對象,這也就是說,string
在與int
相加的時候,會造成裝箱操作。而ToString()
會產生28B的GC,裝箱會產生20B的GC,所以在拼接時,顯式的調用ToString()
可以規避掉裝箱的過程,使用string+int.ToString()
會比string+int
更加節省性能
【字元串性能相關的操作】
1.創建空字元串用用string s = string.Empty
,而不是string s = ""
2.高頻字元串拼接用stringbuilder
,或者字元串格式化string.Format()
而string.Format()
實際上就是利用stringbuilder
去實現的
3.ToUpper
、ToLower
這類方法均會重新生成字元串,看看是否可以避免使用
4.true判斷時,用"value" == string
是最快的;false判斷時,用"value".Equals(string)
是最快的