CLR中字串不變性的最佳化

來源:互聯網
上載者:User
CLR中字串不變性的最佳化

http://www.blogcn.com/user8/flier_lu/index.html?id=1269085&run=.06FE977

    自從有程式設計語言以來,如何處理字串就一直是一個爭論不休的問題。從C/C++用字元數組表示字串,讓使用者完全控制其生命週期;到Delphi/VB通過編譯器內建支援,使用引用計數自動維護字串生命週期;再到Java/C#通過不可變字串以及記憶體回收管理生命週期。不同的策略有著不同的傾向性,也有各自的缺點和優點。這兒我不想評論多種策略之間的優劣,只是想針對C#的實現做一點點較為深入的探討。
     
     CLR中選擇了和Java類似的不可變字串策略,以簡化生命期維護以及多線程同步問題的處理,但同時也付出了一定的效率和空間上的代價,故而不得不通過編譯器一級定製來最佳化。
     Chris Brumme和Yun Jin在其BLog上討論了需要保障字串不變性(immutability)的原因,並指出通過PInvoke以及unsafe代碼直接修改字串內容可能帶來的危害。
     
     Interning Strings & immutability
     Dangerous PInvokes - string modification
     
     為了提高效率和節約空間,CLR內部實際上維護了一個不可變字串表。在堆中分配的字串可以通過String.Intern函數確保其被加入此表;通過String.IsInterned函數判斷自己是否在表中。如果在表中,則可以通過引用來直接對字串進行比較,大大提高字串比較效率。MSDN上的例子如下    
 

以下為引用:

 // Sample for String.Intern(String)
 using System;
 using System.Text; 

 class Sample {
     public static void Main() {
     String s1 = "MyTest";
     String s2 = new StringBuilder().Append("My").Append("Test").ToString(); 
     String s3 = String.Intern(s2); 
     Console.WriteLine("s1 == '{0}'", s1);
     Console.WriteLine("s2 == '{0}'", s2);
     Console.WriteLine("s3 == '{0}'", s3);
     Console.WriteLine("Is s2 the same reference as s1?: {0}", (Object)s2==(Object)s1); 
     Console.WriteLine("Is s3 the same reference as s1?: {0}", (Object)s3==(Object)s1);
     }
 }
 /**//*
 This example produces the following results:
 s1 == 'MyTest'
 s2 == 'MyTest'
 s3 == 'MyTest'
 Is s2 the same reference as s1?: False
 Is s3 the same reference as s1?: True
 */

 

    
     如果熟悉CLR的Metadata檔案結構的朋友可能立刻會想到,在Metadata表中實際上本來就有#String流和#US流,分別儲存程式中固化的字串和使用者字串。例如上面的"MyTest"字串就會被放入流中直接載入,而CLR動態維護的字串表就是在此基礎上擴充的。
     動態建立的字串,如前面例子中通過StringBuilder構造的字串,則預設放在堆中,只有使用者顯式調用了String.Intern函數,才會被加入到靜態字串表中。查看Rotor的代碼,會發現String.Intern實際上是調用當前線程所在AppDomain的GetOrInternString函數;而進一步調用此AppDomain的字串映射表的GetInternedString函數。
 

以下為引用:

    
 

String.Intern(String str) (bclsystemstring.cs:1194) 
   Thread.GetDomain().GetOrInternString(str)
   
 AppDomain.GetOrInternString(String str) (bclsystemappdomain.cs:1558)
   InternalCall    
   
 BaseDomain::GetOrInternString(STRINGREF *pString) (vmappdomain.cpp:856) 
   m_pStringLiteralMap->GetInternedString(pString, )   

 AppDomainStringLiteralMap::GetInternedString() (vmstringliteralmap.cpp:196)

    
     在GetOrInternString函數中:首先會根據字串的內容計算出其HashCode;然後使用此HashCode在當前AppDomain的字串映射表(m_StringToEntryHashTable)中搜尋;如果沒有找到則進一步在CLR的全域字串映射表(SystemDomain::GetGlobalStringLiteralMap())中搜尋;如果還是沒有找到,則根據參數決定是否將此字串以HashCode為索引加入全域字串映射表(GetInternedString函數中根據參數bAddIfNotFound判斷是否添加);如果當前AppDomain可能被卸載,則還會將此字串以HashCode為索引加入到當前AppDomain的局部字串映射表中。虛擬碼如下:
 

以下為引用:

 STRINGREF *AppDomainStringLiteralMap::GetInternedString(STRINGREF *pString, BOOL bAddIfNotFound, BOOL bAppDomainWontUnload)
 {
   StringLiteralEntry *Data;
   DWORD dwHash = m_StringToEntryHashTable->GetHash(字串資料);
   
   if (m_StringToEntryHashTable->GetValue(&StringData, &Data, dwHash))
   {
     return Data->GetStringObject();
   }
   else
   {
     StringLiteralEntry *pEntry = SystemDomain::GetGlobalStringLiteralMap()->GetInternedString(pString, dwHash, bAddIfNotFound);
     
     if(pEntry)
     {
       if (!bAppDomainWontUnload)
       {
         m_StringToEntryHashTable->InsertValue(&StringData, (LPVOID)pEntry, FALSE);
       }
     }
     else
     {
       return pEntry->GetStringObject();
     }
   }
 }

 

    
     另外一個函數String.IsInterned實際上調用路徑完全一樣,只是在GetInternedString沒有在字串映射表搜尋到字串時不自動加入(bAddIfNotFound = false)。
     
     由此我們可以得出一些結論:
     1.Intern String的範圍是整個CLR,雖然每個AppDomain有獨立的優先緩衝機制。這樣既可以保障查詢效率,又可以保障在不同層級(如CLR/AppDomain)載入的共用的Assembly中字串的一致性。
     2.Intern String中的內容直接決定其HashCode,進而決定其在字串表中的儲存和索引,直接內容修改可能導致未知問題。直接修改內容後再使用String.IsInterned,就會返回一個和以前完全不同的索引項目。
     3.Intern String可以通過其引用直接比較。因為在隱式(固化在Metadata的#String或#US流中)或顯示(調用String.Intern)將字串Intern的時候,內容相同的字串都會被定位到字串索引表的同一入口,返回相同的對象引用。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.