C#中string關鍵字相信大家都不陌生,很熟悉。今天主要講的內容呢,就是圍繞着這個簡單的關鍵字。
string在C#中是用來定義字符串變量的關鍵字,很普通,普通得大家可以忽視她,如果你忽視了她,那么你已經失去了一段美好的回憶。下面我們就來追溯這段回憶吧。
首先介紹兩個概念:1、字符串池,2、字符串駐留池
1、 字符串池是編譯器為執行過程中程序映像和內存中的相同字符串創建單個副本,從而得到較小的程序。
2、 字符串駐留池是CLR初始化時創建一個內部哈希表,表的key是字符串,value是托管堆中String的引用,該表就是駐留池。因為字符串是不變量,如果內存中存在多個相同字符串的實例,便會造成內存的浪費,故高級語言(C#、Java)等都用了字符串駐留池來提高內存利用率。
在這里首先介紹這兩個概念的用意是為了告訴大家字符串池和字符串駐留池是不同的概念,不可混淆。
字符串在.Net世界里是如何存儲的呢?
我們先看如下流程圖:
1、
說明:我們以上操作創建了一個”AaronPan”不變量字符串,然后經C#編譯器字符串池機制優化了程序集(這里也可以設置編譯器關閉字符串池機制[C++編譯器默認關閉字符串池],我們針對一般情況,特殊情況忽略),當程序集加載到應用程序域時,CLR2.0版本以上默認會駐留程序集中不變量字符串。
2、
說明:eng_Name並沒有創建新的字符串,eng_Name和name引用了托管堆中同一個不變量字符串。
3、
說明:name,eng_Name,full_Name引用了0x~位置的字符串實例,而my_Name則引用了0x$位置的字符串實例。因為full_Name由兩個不變量字符串串聯起來的,編譯器直接認為是”AaronPan”,而my_Name由兩個變量串聯起來,我們查看IL可以發現兩個變量通過Concat()創建了一個新的字符串。
4、
說明:這里采用String類Intern方法訪問字符串駐留池。因為駐留池中已經存在一個完全相同的字符串,所以返回匹配字符串的引用,而0x$位置的實例則等待GC回收。
注:
1、 字符串駐留池引用的字符串不會被垃圾回收器回收,其生命周期會伴隨着應用程序進程終止而終止。
2、 字符串駐留池雖然可以提高內存利用率,但他要額外維護一個內部哈希表,性能必定會下降,我們可以在C#編譯器設置不開啟字符串駐留功能(CompilationRelaxations.NoStringInterning都被編譯器忽略了,不知道是不是JIT搞的鬼)。根據實際場景,合理應用。
——Aaron.Pan