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》整理

  • 相关阅读:
    【其它】 数学是什么?
    【其它】 MathJax
    FreeCodeCamp练习笔记
    Docker容器操作中常用命令集合
    【转载】Ubuntu 系列安装 Docker
    U盘操作系统,Kali Linux操作系统安装
    网络安全&信息安全&系统安全常用名词汇总
    【CISP笔记】安全漏洞与恶意代码(2)
    【CISP笔记】安全漏洞与恶意代码(1)
    【CISP笔记】数据库及应用安全
  • 原文地址:https://www.cnblogs.com/whbk/p/6433554.html
Copyright © 2011-2022 走看看