日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

字符串的驻留(String Interning)

發布時間:2025/3/14 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 字符串的驻留(String Interning) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

關于字符串的駐留的機制,對于那些了解它的人肯定會認為很簡單,但是我相信會有很大一部分人對它存在迷惑。在開始關于字符串的駐留之前,先給出一個有趣的Sample:

Code Snip:

static?void?Main(string[]?args)
????????
{
????????????
string?str1?=?"ABCD1234";
????????????
string?str2?=?"ABCD1234";
????????????
string?str3?=?"ABCD";
????????????
string?str4?=?"1234";
????????????
string?str5?=?"ABCD"?+?"1234";
????????????
string?str6?=?"ABCD"?+?str4;
????????????
string?str7?=?str3?+?str4;

????????????Console.WriteLine(
"string?str1?=?\"ABCD1234\";");
????????????Console.WriteLine(
"string?str2?=?\"ABCD1234\";");
????????????Console.WriteLine(
"string?str3?=?\"ABCD\";");
????????????Console.WriteLine(
"string?str4?=?\"1234\";");
????????????Console.WriteLine(
"string?str5?=?\"ABCD\"?+?\"1234\";");
????????????Console.WriteLine(
"string?str6?=?\"ABCD\"?+?str4;");
????????????Console.WriteLine(
"string?str7?=?str3?+?str4;");

????????????Console.WriteLine(
"\nobject.ReferenceEquals(str1,?str2)?=?{0}",?object.ReferenceEquals(str1,?str2));
????????????Console.WriteLine(
"object.ReferenceEquals(str1,??\"ABCD1234\")?=?{0}",?object.ReferenceEquals(str1,?"ABCD1234"));

????????????Console.WriteLine(
"\nobject.ReferenceEquals(str1,?str5)?=?{0}",?object.ReferenceEquals(str1,?str5));
????????????Console.WriteLine(
"object.ReferenceEquals(str1,?str6)?=?{0}",?object.ReferenceEquals(str1,?str6));
????????????Console.WriteLine(
"object.ReferenceEquals(str1,?str7)?=?{0}",?object.ReferenceEquals(str1,?str7));

????????????Console.WriteLine(
"\nobject.ReferenceEquals(str1,?string.Intern(str6))?=?{0}",?object.ReferenceEquals(str1,?string.Intern(str6)));
????????????Console.WriteLine(
"object.ReferenceEquals(str1,?string.Intern(str7))?=?{0}",?object.ReferenceEquals(str1,?string.Intern(str7)));
????????}


下邊是輸出的結果:


接下來我們來逐句地分析這段代碼:

首先我們創建了兩個完全相同的字符串 (ABCD1234),并將他們分別賦予了兩個字符創變量——str1和str2。然后把它們傳給了object.ReferenceEquals。我們 知道object.ReferenceEquals是用于確定兩個變量是否具有相同的引用——換句話說,當兩個變量引用的是同一塊托管推的內存快的時候, 返回True,否則返回False。

string?str1?=?"ABCD1234";
string?str2?=?"ABCD1234";
object.ReferenceEquals(str1,?str2)=?True;
object.ReferenceEquals(str1,?"ABCD1234"))?=?True;

令我們感到奇怪的是,當我們分別創建的引用類型 兩個變量——string是引用類型。照理說CLR會在托管堆(Managed Heap)中為它們分配兩段內存快,他們不可能具有相同的引用才對,但是為什么object.ReferenceEquals 方法會返回True呢。而對于第二個比較——一個字符串變量和一個和他具有相同內容的字符串("ABCD1234";)直接進行比較,按照我們對CLR內 存的分配的一般理解,應該是CLR首先會在托管堆中為這段字符串("ABCD1234")分配內存快,然后把相對應的引用傳遞給 object.ReferenceEquals方法(由于分配在托管堆的這段字符串并沒有被任何變量引用,所以當垃圾回收的時候會被回收掉),所以無論如 何也不應該返回True。?

我們先把問題留到最后,接著分析我們的Sample。上面們對字符串變量之間以及變量與字符串之間進行了比較,如果我們對一個字符串變量和一個動態創建的字符串(通過+Operator把兩個字符串連接起來)進行比較,結果又會如何呢?我們來看看下面的偽代碼演示:?

string?str3?=?"ABCD";
string?str4?=?"1234";
string?str5?=?"ABCD"?+?"1234";?
string?str6?=?"ABCD"?+?str4;
string?str7?=?str3?+?str4;
object.ReferenceEquals(str1,?str5)?=?True
object.ReferenceEquals(str1,?str6)?=?False
object.ReferenceEquals(str1,?str7))?=?False

在上面的例子中,我們用三種不同的方式創建了3 個字符串變量(str5,str6,str7)——string+string;string+variable;variable+variable。 然后分別和我們已經創建的、和它們具有相同字符串“值”的變量(str1)作比較。同樣令我們感到奇怪的是第一個返回True,而后兩個則為False。 帶著這些疑惑我們來看看對于string這一特殊的類型說采用的特殊的使用機制。?

1. System.String雖然是一個引用類型,但是它具有其自身的特殊性。我們最容易想到的是它創建的特殊性——一般的對象在創建的時候需要通過new關鍵字調用對應的構造函數來實現;而創建一段string不需要這么做——我們只需要把對應的字符串賦給對應的字符串變量就可以了。之所以存在著這種差異,是因為他們在創建過程中使用的IL指令是不同的——一般的引用對象的創建是通過newobj這樣一個IL指令來實現的,而創建一個字符串變量的IL指令則是ldstr (load string)。(象C#,VB.NET這樣的語言畢竟是高級語言,進行了高度的抽象,站在這樣的角度分析問題往往不能夠看到其實質,所以有時候我們把應該從交底層上面找突破口——比如分析IL,Metadata…);

2. 由于String是我們做到頻率最高的一種類型,CLR考慮性能的提升和內存節約上,對于相同的字符串,一般不會為他們分別分配內存塊,相反地,他們會共 享一塊內存。CLR實際上采用這個的機制來實現的:CLR內部維護著一塊特殊的數據結構——我們可以把它看成是一個Hash table,這個Hash table維護者大部分創建的string(我這里沒有說全部,因為有特例)。這個Hash table的Key對應的相應的string本身,而Value則是分配給這個string的內存塊的引用。當CLR初始化的時候創建這個Hash table。一般地,在程序運行過程中,如果需要的創建一個string,CLR會根據這個string的Hash Code試著在Hash table中找這個相同的string,如果找到,則直接把找到的string的地址賦給相應的變量,如果沒有則在托管堆中創建一個string,CLR 會先在managed heap中創建該strng,并在Hash table中創建一個Key-Value Pair——Key為這個string本身,Value為這個新創建的string的內存地址,這個地址最終被賦給響應的變量。這樣我們就能解釋上面的疑問了。?

string?str1?=?"ABCD1234";
string?str2?=?"ABCD1234";
object.ReferenceEquals(str1,?str2)=?True;
object.ReferenceEquals(str1,?"ABCD1234"))?=?True;

當創建str1的時候,CLR先在我們上面提到的Hash table中找“ABCD1234”這樣的一個string,沒有找到,則在托管堆中為這個string分配一塊內存,然后在Hash table為該string添加一個Key-Value Pair。接著創建str2,CLR仍然會在Hash table找ABCD1234這樣的一個string,這回它會找到我們新創建的這個Entry,所以這個Key-Value Pair中Value(string的地址)會賦給str2。因為str1和str2 具有相同的引用,所以調用object.ReferenceEquals返回True。同理當我們對str1和"ABCD1234"進行比較的時 候,str1直接傳入該方法,放傳入"ABCD1234"這個字符串的時候,CLR同樣會在Hash table找ABCD1234這樣的一個string,相同的Entry被找到,這個Entry(Key-Value Pair)的Value(string的地址)被傳到object.ReferenceEquals,所以他們仍然相同的引用,結果返回True。?

3. 并非所有的情況下字符串的駐留都會起作用。對于一個動態創建的字符串(比如string+variable;variable+variable),駐留機制便不會起作用。因為對于這樣的字符串,是不會被添加到內部的Hash table中的。但是對于string+string則不同,因為當這樣的語句被編譯成IL的時候,編譯器是先把結構計算出來,然后再調用ldstr指令 ——而對于string+variable;variable+variable這種情況,所對應的IL指令是Concat。所以對于 string+string字符串的駐留仍然有效。

比如對于以下一段代碼:

??static?void?Main(string[]?args)
????????
{
????????????
string?str1?=?"ABC";
????????????
string?str2?=?str1?+?"123";
????????????
string?str3?=?"ABC"?+?"123";
}

對應的IL Code是:

.method?private?hidebysig?static?void??Main(string[]?args)?cil?managed
{
??.entrypoint
??
//?Code?size???????26?(0x1a)
??.maxstack??2
??.locals?init?([
0]?string?str1,
???????????[
1]?string?str2,
???????????[
2]?string?str3)
??IL_0000:??nop
??IL_0001:??ldstr??????
"ABC"
??IL_0006:??stloc.
0
??IL_0007:??ldloc.
0
??IL_0008:??ldstr??????
"123"
??IL_000d:??call???????
string?[mscorlib]System.String::Concat(string,
??????????????????????????????????????????????????????????????
string)
??IL_0012:??stloc.
1
??IL_0013:??ldstr??????
"ABC123"
??IL_0018:??stloc.
2
??IL_0019:??ret
}
?//?end?of?method?Program::Main


所以現在我們就可以解釋第二個疑問了。

雖然對于對一個動態創建的字符串(比如 string+variable;variable+variable),駐留機制便不會起作用。但是我們可以手工的啟用駐留機制——那就是調用定義的 System.String中的靜態方法Intern。這個方法接受一個字符串作為他的輸入參數,返回經過駐留處理的string。他的實現機制是:如果能在內部的Hash Table中找到傳入的string,則返回對應的string引用,否則就在Hash Table添加該string對應的Entity,并返回string的引用。所以下面的代碼就不難解釋了。

????????????Console.WriteLine("\nobject.ReferenceEquals(str1,?string.Intern(str6))?=?{0}",?object.ReferenceEquals(str1,?string.Intern(str6)));
????????????Console.WriteLine(
"object.ReferenceEquals(str1,?string.Intern(str7))?=?{0}",?object.ReferenceEquals(str1,?string.Intern(str7)));

?相關內容:
[原創]如何改Managed Code的Performance和Scalability系列之二:深入理解string和如何高效地使用string

?

原文URL:http://www.cnblogs.com/artech/archive/2007/03/04/663728.aspx

轉載于:https://www.cnblogs.com/jeriffe/articles/1962247.html

總結

以上是生活随笔為你收集整理的字符串的驻留(String Interning)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。