開局兩張圖,內容全靠刷!
馬甲哥看到這樣的現象,一開始還是有點懵逼。
這個例子,string是純粹的引用型別,但是在函式傳值時類似於值傳遞;
我之前給前後示例的記憶體變化圖吧:
根因就是大多數高階語言都把String設計成不可變的:由一個字串池管理字串面值。
為什麼被設計成不可變。
總結起來:
string 被設計為不可變, 是因為 string在現代任何語言中,使用很頻繁:多個物件可能都是這個字元面值, 然後就設計一個Pool來儲存string。
既然pool裡面共享字元面值,修改的時候又不能影響到別人,那就只好重新拷貝產生新的字元面值。不可變資源消除了多執行緒中的資源競爭,對於文字的修改都會導致建立新空間,因此在多個執行緒同時訪問文字無需設定鎖,這對頻繁使用的string開發者很友好。
字串不變性對於[在雜湊表中使用字串作為鍵]很友好,需要計算雜湊值的物件必須是不可變的,以確保雜湊值不變。
- 一個有意思的現象是: String雖然是引用型別,字串對比時卻表現的像值型別
string str1="FooFoo";
string strFoo="Foo";
string str2= strFoo + strFoo;
return str1 == str2; // 返回true
正因為String不可變性 & Pool的機制,頻繁變更字串,會在池中產生很多臨時的不用的字串,所以我們有了緩解的套路:
StringBuilder
代表可變的字串,一旦修改不會嘗試建立新物件,而是動態擴充套件記憶體
var ss = new StringBuilder("Hello ", 100); // 初次字元容量100
ss.Append("www.cnblogs.com");
Console.WriteLine(ss.ToString()); // ss列印結果為:Hello www.cnblogs.com
Span
Span該出圈了,
Span提供對記憶體連續區域的型別安全訪問,該記憶體可以位於堆、堆疊、甚至是非託管記憶體;
與String不可變性相關的是ReadOnlySpan
(值型別), 提供記憶體資料的只讀檢視,每次切片不會產生新物件,而是在存在的連續空間上創造新的檢視。
var text = "https://www.cnblogs.com/JulianHuang/p/14817621.html";
ReadOnlySpan<char> nameSpan = text.AsSpan(8, 15);
nameSpan = nameSpan.Slice(4,7);
Console.WriteLine(nameSpan.ToString());
總結輸出
今天從兩張詭異的程式設計圖聊到了String的不可變性、記憶體分佈, 延伸談到了
- String不可變性的設計設計考量(有先射箭再畫靶的嫌疑️)
- 針對頻繁修改的String如何做記憶體優化
不是自吹,文章內容在業界相當硬核(多次被各大佬/CSDN點贊/轉載),閱讀/關注不是目的,更希望得到更多的閱讀反饋、互相促進認知的提升(相當真誠️)。