.NET中的字符串駐留池介紹
在.NET中,對(duì)于相同的字符串,.NET會(huì)將它們指向同一個(gè)地址,它們是相同的實(shí)例。.NET中的字符串并不會(huì)更新,當(dāng)更改一個(gè)字符串變量時(shí),由于字符串的不可變性,.NET實(shí)際上是新創(chuàng)建一個(gè)字符串,而將變量地址指向新創(chuàng)建的字符串地址。
看下面的一個(gè)例子:
using System; namespace ConsoleApp2 { class Program { static void Main(string[] args) { string str1 = "hello"; string str2 = "hello"; bool tf = object.ReferenceEquals(str1, str2); Console.WriteLine(tf); Console.ReadKey(); } } }
程序執(zhí)行結(jié)果
從執(zhí)行結(jié)果我們可以得出結(jié)論:str1和str2指向同一個(gè)內(nèi)存對(duì)象,它們是同一個(gè)實(shí)例。
在.NET中,CLR默默地維護(hù)了一個(gè)叫做駐留池(Intern Pool)的表。這個(gè)表記錄了所有在代碼中使用字面量聲明的字符串實(shí)例的引用。這說(shuō)明使用字面量聲明的字符串會(huì)進(jìn)入駐留池,而其他方式聲明的字符串則不會(huì)進(jìn)入駐留池,也就不會(huì)自動(dòng)享受到CLR防止字符串冗余的機(jī)制的好處了。
看下面一個(gè)例子
using System; using System.Text; namespace ConsoleApp2 { class Program { static void Main(string[] args) { StringBuilder sb = new StringBuilder(); sb.Append("he").Append("llo"); string str1 = "hello"; string str2 = sb.ToString(); bool tf = Object.ReferenceEquals(str1, str2); Console.WriteLine(tf); bool tf1 = str1 == str2; Console.WriteLine($"str1和str2的內(nèi)容是否相同:{tf1}"); Console.ReadKey(); } } }
程序執(zhí)行結(jié)果
這里輸出了False。雖然str1和str2是相同的字符串,但是由于str2不是通過(guò)字面量的方式聲明的,CLR在為ToString()返回值分配內(nèi)存時(shí),并不會(huì)到駐留池中去檢查是否有值為"hello"的字符串已經(jīng)存在了,所以也不會(huì)讓str2指向駐留池中的對(duì)象。
如果希望強(qiáng)制CLR檢查駐留池,以避免冗余的字符串副本,String類的設(shè)計(jì)者提供了一個(gè)名為Intern的類方法。下面是該方法的一個(gè)實(shí)例
using System; using System.Text; namespace ConsoleApp2 { class Program { static void Main(string[] args) { StringBuilder sb = new StringBuilder(); sb.Append("he").Append("llo"); string str1 = "hello"; // 在這里強(qiáng)制檢查字符串駐留池 string str2 = string.Intern(sb.ToString()); bool tf = Object.ReferenceEquals(str1, str2); // 輸出True,因?yàn)闄z查駐留池時(shí),發(fā)現(xiàn)字符串已經(jīng)存在 Console.WriteLine(tf); bool tf1 = str1 == str2; Console.WriteLine($"str1和str2的內(nèi)容是否相同:{tf1}"); Console.ReadKey(); } } }
程序執(zhí)行結(jié)果
Intern方法接受一個(gè)字符串作為參數(shù),它會(huì)在駐留池中檢查是否已經(jīng)存在參數(shù)所表示的字符串。如果存在,則返回那個(gè)駐留池中的字符串的引用;否則向駐留池中加入一個(gè)新的表示相同值的字符串,并返回這個(gè)字符串的引用。
不過(guò)要注意的是,就算Intern方法在駐留池中找到了相同值的字符串,也不能讓您省卻一次字符串內(nèi)存分配的操作,因?yàn)樽鳛閰?shù)的字符串已經(jīng)被分配了一次內(nèi)存了。使用Intern方法的好處在于,如果Intern方法在駐留池中找到了相同值的字符串,此時(shí)雖然在內(nèi)存中存在兩份該字符串的副本(一份是參數(shù),一份是駐留池中的),但是隨著時(shí)間的流逝,參數(shù)所引用的那個(gè)副本會(huì)被垃圾回收掉,這樣對(duì)于該字符串內(nèi)存中就不存在冗余了。
當(dāng)你的程序中存在某個(gè)方法,可以根據(jù)不同的上下文環(huán)境創(chuàng)建并返回一個(gè)很長(zhǎng)的字符串,而在程序運(yùn)行的過(guò)程中它會(huì)經(jīng)常返回同樣的字符串時(shí),你可能就要考慮使用Intern方法來(lái)提高內(nèi)存的使用率了。不過(guò)同樣值得注意的是,使用Intern方法讓一個(gè)字符串存留于駐留池中也有一個(gè)副作用:即使已經(jīng)不存在任何其它引用指向駐留池中的字符串了,這個(gè)字符串仍然不一定會(huì)被垃圾回收掉。也就是說(shuō)即使駐留池中的字符串已經(jīng)沒有用處了,它可能也要等到CLR終結(jié)時(shí)才被銷毀。當(dāng)你使用Intern方法的時(shí)候,也應(yīng)該考慮到這個(gè)特殊的行為。
到此這篇關(guān)于.NET字符串駐留池的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
.NET?6全新配置對(duì)象ConfigurationManager介紹
這篇文章介紹了.NET?6全新配置對(duì)象ConfigurationManager,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-11-11如何在WebForm中使用javascript防止連打(雙擊)
如何在WebForm中使用javascript防止連打(雙擊)...2007-01-01解析ABP框架領(lǐng)域?qū)又械膶?shí)體類與倉(cāng)儲(chǔ)類
基于ASP.NET的ABP框架遵循DDD領(lǐng)域驅(qū)動(dòng)設(shè)計(jì),其中就有一個(gè)領(lǐng)域?qū)拥母拍?這里我們就來(lái)解析ABP框架領(lǐng)域?qū)又械膶?shí)體類與倉(cāng)儲(chǔ)類:2016-06-06Effective C# 使用成員初始化器而不是賦值語(yǔ)句
一般情況下,一個(gè)類都會(huì)有多個(gè)構(gòu)造函數(shù)。隨著時(shí)間的推移,成員變量、構(gòu)造函數(shù)不斷增加2012-11-11ADO.NET 連接數(shù)據(jù)庫(kù)字符串小結(jié)(Oracle、SqlServer、Access、ODBC)
ADO.NET 連接數(shù)據(jù)庫(kù)字符串小結(jié),包括Oracle、SqlServer、Access、ODBC,需要的朋友可以收藏下2012-04-04