浅谈C#中字符串的驻留池机制

了解C#的都知道,CLR对于String类有一种特别的内存管理机制:有时候,明明声明了两个String类的对象,可是他们恰恰却指向同一个实例。以下:编程

String s1 = "Hello";优化

String s2 = "Hello"; //s2和s1的实际值都是“Hello”ui

bool same = (object) s1 == (object) s2; //这里比较s一、s2是否引用了同一个对象实例设计

//因此不能写做bool same = s1 == s2;对象

//由于String类重载了==操做符来比较String对象包含的实际值内存

这里的same会被赋值为true。也就是说s1真的和s2引用了同一个String对象。固然,应该注意到的是s1和s2都被统一赋值为同一个字符串“Hello”,这才是出现上述状况的缘由。字符串

如今咱们初步得出结论,当有多个字符串变量包含了一样的字符串实际值时,CLR可能不会为它们重复地分配内存,而是让它们通通指向同一个字符串对象实例。(这里我说了“可能”,是由于某些状况下,确实也会发生同一个字符串实际值在内存中有多份副本同时存在。请继续往下看。)string

咱们知道,String类有不少特别的地方,其中之一就是它是“不会改变的”(immutable)。这说明在咱们每次对一个String对象进行操做时(好比说使用Trim,Replace等方法),并非真的对这个String对象的实例进行修改,而是返回一个新的String对象实例做为操做执行的结果。String对象的实例一经生成,到死都不会被改变了!内存管理

基于String类这样的特性,CLR让表示相同的字符串实际值的变量指向同一个String事例,就是彻底合理的了。由于利用任何一个对String实例的引用所进行的修改操做都不会切实地影响到该实例的状态,也就不会影响到其余全部指向该实例的引用所表示的字符串实际值。CLR如此管理String类的内存分配,能够优化内存的使用状况,避免内存中包含冗余的数据。table

为了实现这个机制,CLR默默地维护了一个叫作驻留池(Intern Pool)的表。这个表记录了全部在代码中使用字面量声明的字符串实例的引用。这说明使用字面量声明的字符串会进入驻留池,而其余方式声明的字符串并不会进入,也就不会自动享受到CLR防止字符串冗余的机制的好处了。这就是我上文提到的“某些状况下,确实也会发生同一个字符串实际值在内存中有多份副本同时存在”的例子。请看这个例子:

StringBuilder sb = new StringBuilder();

sb.Append("He").Append("llo");

string s1 = "Hello";

string s2 = sb.ToString();

bool same = (object) s1 == (object) s2;

这时same就不是true了,由于虽然s1,s2表示的是相同的字符串,可是因为s2不是经过字面量声明的,CLR在为sb.ToString()方法的返回值分配内存时,并不会到驻留池中去检查是否有值为“Hello”的字符串已经存在了,因此天然不会让s2指向驻留池内的对象。

为了让编程者可以强制CLR检查驻留池,以免冗余的字符串副本,String类的设计者提供了一个名为Intern的类方法。下面是该方法的一个示例:

StringBuilder sb = new StringBuilder();

sb.Append("He").Append("llo");

string s1 = "Hello";

string s2 = String.Intern(sb.ToString());

bool same = (object) s1 == (object) s2;

好了,same又是true了。Intern方法接受一个字符串做为参数,它会在驻留池中检查是否存在参数所表示的字符串。若是存在,则返回那个驻留池中的字符串的引用;不然向驻留池中加入一个新的表示相同值的字符串,并返回这个字符串的引用。不过要注意的是,就算Intern方法在驻留池中找到了相同值的字符串,也不能让您省却一次字符串内存分配的操做,由于做为参数的字符串已经被分配了一次内存了。而使用Intern方法的好处在于,若是Intern方法在驻留池中找到了相同值的字符串,此时虽然在内存中存在两份该字符串的副本(一份是参数,一份是驻留池中的),可是随着时间的流逝,参数所引用的那个副本会被垃圾回收掉,这样对于该字符串内存中就不存在冗余了。

当您的程序中存在某个方法,能够根据不一样的上下文环境建立并返回一个很长的字符串,而在程序运行的过程当中它有会常常返回一样的字符串时,您可能就要考虑考虑使用Intern方法来提升内存的利用率了。

不过一样值得注意的是,使用Intern方法让一个字符串存活于驻留池中也有一个反作用:即便已经不存在任何其它引用指向驻留池中的字符串了,这个字符串仍然不必定会被垃圾回收掉。也就是说即便驻留池中的字符串已经没有用处了,它可能也要等到CLR终结时才被销毁。当您使用Intern方法的时候,也应该考虑到这个特殊的行为。

相关文章
相关标签/搜索