学习的时候总是遇到一些问题,可能当时想通了,可是过一段时间后便有忘却了,因此又得重复找资料去学习,这样实在是麻烦,故抽点时间将自己知道的东西写成笔记保存下来,这样既能加深自己对知识的理解,同时也能将自己所知道的东西一起与大家分享,一举两得,何乐而不为。

本文将从三个方面(提出问题、分析与解决问题、问题的扩展)来探讨String字符串驻留机制,与大家一起学习。


一、提出问题:

我们先来看看下面的代码:(假设是一道考题)


字符串驻留转载 - iplaydotnet - iplaydotnet的博客   String str="True";
   字符串驻留转载 - iplaydotnet - iplaydotnet的博客If ( Object.ReferenceEquals("True",str)==true)
   字符串驻留转载 - iplaydotnet - iplaydotnet的博客    Console.WriteLine("That is True");
   字符串驻留转载 - iplaydotnet - iplaydotnet的博客Else
   字符串驻留转载 - iplaydotnet - iplaydotnet的博客    Console.WriteLine("That is False");
字符串驻留转载 - iplaydotnet - iplaydotnet的博客

留给我们的答案是:1、That is True  2、That is False 我们是选择1还是选择2呢?我们没有机会去调试,摆在我们面前的只有是True或False。我们能够肯定Object.ReferenceEquals("True",str)==true,便会毫不犹豫地选择答案1,否则选择答案2;

你无须紧张也不必担心失去什么,你可以静下心来思考问题。即便你毫无办法,你也有50%的机会选择正确的答案。其实什么样的答案并不重要,最重要的是我们扪心自问自己是否真的知道得到答案的理由。


二、分析与解决问题:

要解决上面的问题,我们不得不对String类型的字符串驻留机制有一个清楚的了解:

我们都知道String类型直接继承自Object,是一个引用类型。但是编译器在处理字符类型的变量时直接用文本常量来表达。编译器会将这些文本常量字符串存放在托管模块的元数据中,然后在运行时使用字符串驻留机制来访问他们。

字符串驻留机制的提出:字符串比较是许多应用程序中常用的操作,也是一个可以极大地损伤系统性能的操作。针对字符串是的恒定性(也就是一个字符串一旦被创建,我们就不可能再将其变长、变短、或者改变其中任何的字符),我们在内存中只存储一个文本常量字符,并且所有对该文本常量字符串的引用都指向该字符串对象,这样便可提高系统性能,节约内存的使用。

分析上面代码:当CLR初始化时,它会创建一个内部的散列表,其中的键为字符串,值为指向托管堆中字符串对象的引用。刚开始,该表为空(当然)。当JIT编译器编译方法时,它会在散列表中查找每一个文本常量字符串。在上面的代码中,编译器首先会查找第一个"True"字符串,并且因为它没有找到,它便会在托管堆中构造一个新的String对象(指向该字符串),然后将"True"字符串和指向该对象的引用添加到散列表。接着,JIT编译器在散列表中查找第二个"True"字符串,这一次由于会找到该字符串,所以不会执行任何操作。

代码执行时,它会在第一行发现需要一个"True"字符串引用。于是,CLR便在其内部的散列表中查找"True",并且会找到它,这样指向先前创建的String对象的引用就被保存在变量str中。当执行到第二行代码时,CLR会再一次在其内部的散列表中查找"True",并且仍然会找到它。于是,指向同一个String对象的引用会被传递给Object的静态方法ReferenceEquals作为第一个参数,自然该操作将返回true,因此得到答案便是"That is True"..


三、问题的扩展(Extends)

Object的静态方法ReferenceEquals与String的实例方法Equals比较:

1、  ReferenceEquals只有在两个引用指向同一个对象时才会返回true.

2、  Equals当两个字符串具有同样的字符集时便返回true.

3、  ReferenceEquals执行的效率要高于Equals。

String类的两个静态方法允许我们将含有相同字符集的动态字符串变为托管堆中的一个字符对象,从而提升系统性能。

Public static String Intern(String str);

Public static String IsIntern(String str);

Intern方法:接受一个String参数,然后在CLR内部的散列表中查找它。如果能够找到该字符,Intern将返回已经存在的String对象的引用。如果不存在,则添加到CLR内部的散列表中,Intern最后返回自身的引用。(垃圾收集器将可以回收占用内在)

IsIntern方法:也接受一个String作为参数,并会在CLR内部的散列表中查找它。如果CLR内部散列表中含有该字符串,IsIntern将返回散列表中保存的字符串对象引用。如果散列表中不含该字符串,则返回null,并不会将该字符串添加到散列表中。

注意:

1、C#编译器使用IsIntern方法对Switch/case语句进行了性能优化。

2、只有当我们需要在应用程序中多次比较同一个字符串时,我们才应该运用字符串驻留技术。否则,系统性能会因此而损伤。

 


   结束语:
   通过自己的整理与梳理,对字符串驻留算是有了一个很好的理解,但是在整理的过程中发现自己对"CLR初始化时创建散列表"还存有不明白之处,希望大家能给予帮助。
if ($ != jQuery) { $ = jQuery.noConflict(); }
posted on 2010-10-09 21:43  深海大虾  阅读(389)  评论(0编辑  收藏  举报