我很早以前就已经了解到字符串驻留技术能够优化内存占用大小,但都印象不深,今天花了一些时间对这个问题进行的研究,加深了对它的理解。这里就总结一下,便于以后学习和查找,也希望能够给朋友们带来帮助。谢谢!
MSDN概念:公共语言运行库通过维护一个表来存放字符串,该表称为拘留池,它包含程序中以编程方式声明或创建的每个唯一的字符串的一个引用。因此,具有特定值的字符串的实例在系统中只有一个。
上面的概念不好理解,我们还是从基础说起:
一、众所周知,C#中的string是一个引用类型,String对象存放在堆上,而不是在堆栈上的,因此,当把一个字符串变量赋给另一个字符串时,会得到内存中同一个字符串的2个引用。但是为什么我们修改一个字符串的值,而另一个字符串的值不受影响呢?原来当我们把一个字符串的值赋给另一个字符串时候,就会创建一个全新的String对象,就是说是分别指向堆中2个完全不同的地址空间。下面是一个简单例子:
{
string s1 = "Charles";
string s2 = s1; //注意此时会创建一个新对象
Console.WriteLine("s1=" + s1);
Console.WriteLine("s2=" + s2);
s1 = "Charles Chen Change";
Console.WriteLine("s1=" + s1);
Console.WriteLine("s2=" + s2);
}
结果是:
s1=Charles
s2=Charles
s1=Charles Chen Change
s2=Charles
也就是说,改变s1的值并没有改变s2的值,这实际上是和引用类型是矛盾的。其实当S1="Charles"时候,就在堆上分配了一个String对象,在S2=S1时候,引用也指向这个引用,但是当S1的值发生变化的时候,而不是替换原来的值,实际上是在堆上新分配一个内存空间,S2的值还是指向原来的对象,所以塔的值没有发生变化。
二、然后我们看看下面的代码块:
string str2 = "CharlesChen";
当我们调用System.Object.Equals(str1,str2)时候,返回值是True,根据对上面的理解,按道理说是应该返回为false,str1和str2应该指向不同的内存空间才对。怎么会返回为true呢?这里就引入了"字符串驻留技术"。
其实这里CLR使用了字符串驻留技术,对于string str1="CharlesChen";string str2="CharlesChen";
当CLR初始化时,会创建一个内部的散列表(Hash表),其中的键位字符串,值为指向托管堆中字符串的引用。刚开始,散列表为空,JIT编译器编译方法时,会在散列表中查找每一个文本字符串常量(这里是"CharlesChen"),首先会查找"CharlesChen",并且因为没找到,编译器会在托管堆中构造一个全新的指向"CharlesChen"的对象引用,然后将"abc"字符串和执行该对象的引用添加到散列表中去。
当string str2="CharlesChen"时候,由于前面已经在散列表中加了该"CharlesChen"字符串,所以编译器不会执行任何分配内存空间的操作。首先编译器会在内部的散列表中查找"CharlesChen",并且会找到,这样指向先前创建的String对象的引用就会被找到,并且这里Str2就指向找到的那个引用。因此Str1和Str2就指向了内存中同一个地址的引用。所以System.Ojbect.Equals(str1,str2)就返回为true了。
相关资料:
http://www.cnblogs.com/coderlee/archive/2008/01/02/1023316.html 一个字符串驻留技术的demo,加深对它的理解.
如果有什么问题,欢迎朋友们指正,谢谢!
Charles Chen