[原文地址]主讲字符串池和字符串驻留地
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搞的鬼)。根据实际场景,合理应用。
自我总结
1、Trim()与Replace()
if (cmbTest.SelectedItem.ToString() == "Trim") { txtOutput.Text = txtInput.Text.Trim()+"解释Trim()去除的是字符串两端的空格";}if (cmbTest.SelectedItem.ToString() == "Replace"){ txtOutput.Text = txtInput.Text.Replace(" ", "") + "用空代替空格";}
strText.Replace(" ","123");//用123代替空格
strText.Replace("a","123");//用123代替a