zoukankan      html  css  js  c++  java
  • String — 深入理解String

    String类型是我们常用的基础数据类型之一,他是一个特殊的引用类型,在.NET里面算是少有的异类,那么特殊在什么地方呢?接下来就让深入的了解String

    一、特殊之处

         (1)、创建特殊性:String对象不能使用newobj指令创建,而是ldstr指令创建,在实现机制上,CLR给了特殊的照顾优化内存

         (2)、应用上,String类型表现为值类型,但在内存中,String类型表现为引用类型,存储在托管堆里面

         (3)、两次创建内容相同的String对象可以指向相同的地址

         (4)、String类是跨应用程序域的,可以在不同的应用程序域中访问到同一String对象

         (5)、String类是密封类,不能被继承

         (6)、因为字符串驻留是进程级别的,所以可以跨AppDomin存在,即同一个字符串对象可以在不同的应用程序域被访问,突破了AppDomin的隔离,其原因还是字符串的恒定性,因为是不可变的,所以没必要在隔离

    二、字符串的恒定性

        string对象和其他类型最大的区别就是它的恒定性,指的是:字符串创建后,系统会在托管堆上开辟一块连续空间,我们无法通过人为的方式去修改它,所有对string类型操作的,实际上都是返回了一个新的string,其本身不产生任何差别。

        当然这样有利也有弊:利:保证原string对象的稳定性,不会出现线程同步问题。

                                    弊:创建新的字符串对象,会消耗性能和内存。所以CLR使用了一项技术来解决这个问题——字符串驻留

    三、字符串驻留

          先看代码

    string s1 = "小红";
    string s2 = "" + ""; 
    string s3 = "";
    string s4 = s3 + "";   
    Console.WriteLine(ReferenceEquals(s1, s2));//ReferenceEquals():是Object的一个方法,比较引用是否相等,不要用于值类型比较
    Console.WriteLine(ReferenceEquals(s1, s4));

    执行结果:

    答案是不是有点出乎意料?

        按理说,s1和s2是两个不同的string对象,为什么引用会相同呢?看结果就算是相同的,那么s4的值也是"小红",为什么和s1又不一样了?

    好的,带着我们的问题,让我们首先来了解下什么叫做字符串驻留机制:

       对于相同的字符串,CLR不会为期重新分配内存空间,而是公用同一内存地址。

    那么在内部CLR是怎么实现这一功能的?

         CLR内部维护了一个hash table 来管理其创建的大部分string对象。其中key是string本身,而value为分配给对应的string的内存地址。如下如所示

     现在我们在来回过头看一下那两个结果,为什么是true和false

    第一个结果:s1和s2,在创建s1的时候,哈希表内的key并没有"小红"这个值,所以当JIT编译的时候,会在这个哈希表的key里面添加一个"小红",然后把s1在内存中的地址方法value里面,当创建s2的时候,JIT又在这个哈希表内进行查找,然后发现key里面已经有了这个值,所以,就把key-Value对应的value赋值给了s2,所以说他们两个的引用是相同的,那么为什么第二个结果是false呢?

    第二个原因:虽然s4的结果最后也是"小红",但是在创建的过程中,发生了变化,s4是动态生成的字符串,这样的字符串是不会被添加到哈希表中进行维护的,所以返回为false。

    这样,我们就能很容易的解释上述代码的原因了。

    补充:对于动态生成的字符串,没有添加到CLR的哈希表中导致字符串驻留失效的,我们可以通过两个静态的string方法,手工启用字符串驻留机制

    string s1="小红";
    string s3="";
    //s4 = string.Intern(s3+"红");  //true
    s4 = string.IsInterned(s3+"");  //true
    Console.WriteLine(ReferenceEquals(s1, s4));

    public static String Intern(String str);

         //如果暂存了 str,则返回系统对其的引用;否则返回对值为 str 的字符串的新引用。

    public static String IsInterned(String str);

        //此方法在暂存池中查找 str。 如果已经将 str 放入暂存池中,则返回对此实例的引用;否则返回 null。

    两者的区别:

    同样,如果我们不想使用字符串驻留机制,可以使用string.copy()来解除这一限定

    string s1 = "小红";
    string s2 = string.Copy(s1);
    Console.WriteLine(ReferenceEquals(s1, s2));  //false

    public static String Copy(String str);

    特别指出:字符串驻留进程级别的,可以跨应用程序域(AppDomain)而存在,也就是不同的类里面相同的字符串值同样是同一个引用。垃圾回收不能释放哈希表中的引用字符串对象,只有进程结束这些对象才会被释放

      *根据《你必须知道的.NET》整理

  • 相关阅读:
    FreeCommander 学习手册
    String详解, String和CharSequence区别, StringBuilder和StringBuffer的区别 (String系列之1)
    StringBuffer 详解 (String系列之3)
    StringBuilder 详解 (String系列之2)
    java io系列26之 RandomAccessFile
    java io系列25之 PrintWriter (字符打印输出流)
    java io系列24之 BufferedWriter(字符缓冲输出流)
    java io系列23之 BufferedReader(字符缓冲输入流)
    java io系列22之 FileReader和FileWriter
    java io系列21之 InputStreamReader和OutputStreamWriter
  • 原文地址:https://www.cnblogs.com/whbk/p/6433554.html
Copyright © 2011-2022 走看看