• 05引用类型以及特殊引用类型string


      基本

    □ 哪些属于引用类型

    类(object,string),接口、数组、委托

    □ 引用类型分配在哪里

    ● 引用类型变量位于线程栈。
    ● 引用类型实例分配在托管堆上。
    ● 当引用类型实例的大小小于85000bytes,被分配在GC堆上,当大于或等于85000bytes,被分配在LOH(Large Object Heap)上。


    □ 变量(Variable),对象(Object),实例(Instance)

    变量:
    变量分配在线程栈上。
    变量可以是值类型,也可以是引用类型。
    当变量是引用类型时,包含了对对象的引用(内存地址),也叫做"对象引用"。

    对象:
    对类、接口、委托和数组等的一个抽象描述。

    实例:
    在堆上创建的对象,称为对象实例。

    □ 引用类型没有new意味着什么?

    Object a = null;
    Console.WriteLine(a.ToString());
    运行报错"未将对象引用设置到对象实例"。
    意思是,在线程栈上创建的变量a没有指向到堆上的对象实例。

    □ 托管堆上的垃圾回收

    GC会遍历所有托管堆上的对象,按照一定的递归遍历算法,对那些没有被引用的不可访问对象实施回收。

      new的背后发生了什么

    展开

    另外,引用类型的值,比如这里的引用类型Person中的值_age也被分配在托管堆上。   

    new的过程

      线程栈上的2个变量引用同一个对象实例的内存地址

    线程栈上的2个变量引用同一个对象实例的内存地址,改变其中一个变量的值会影响到另外一个变量。

    展开

    2

      string类型是特殊的引用类型

    □ 特殊性体现在

    从应用角度体现了值类型语义,从内存角度实现为引用类型存储,位于托管堆。

    □ 什么是string
    可以看作是char的集合char[]    

    □ string创建与实例化

    string str = "Hello";

    以下错误
    String str = new String("Hello");
    因为System.String没有提供此构造函数

    以下可以
    Char[] cs = {'a', 'b','c'};
    String strArr = new String(cs);
    但很少使用这种方式。

    □ 字符串的恒定性Immutability

    是指字符串一经创建,就不可改变。
    字符串一旦创建,在托管堆上分配一块连续的内存空间。


    恒定性的好处:
    对String对象的任意操作,不会改变原字符串。
    操作字符串不会出现线程同步的问题。
    成就了字符串驻留。

    恒定性的不足:
    因为恒定性,对字符串的任何操作,比如字符串比较,字符串链接,字符串格式化等都会创建新的字符串,这样造成内存与性能的双重损耗。如下:

    public static void Main() 
    { 
        string str = "This is a test about immuntablility of string type."; 
        Console.WriteLine(str.Inseert(0,"Hi").Substring(19).ToUpper()); 
        Console.WriteLine(str); 
    } 


    由于Insert,Substring,ToUpper这些方法,都会创建出新的临时字符串,而这些新的字符串不被其他代码引用的时候,就会被垃圾回收,造成性能上的损失。

    恒定性的前提,String为密封类:

    public sealed class String:IComparable, ICloneable,IConvertible,IComparable<string>,IEnumerable<char>,IEnumerable,IEquatable<string>


    □ 字符串驻留String Interning

    MSDN对于字符驻留的定义:公共语言运行库通过维护一个哈希表(Hash Table)来存放字符串,该表成为拘留池,也叫驻留池。

    字符串驻留弥补了恒定性的不足:
    对于相同的字符串,CLR不会不会为其分配内存空间,而是共享同一内存。
    CLR内部维护了一个哈希表HashTable来管理其创建的大部分String对象。key是string本身,value是string对应的内存地址。

    驻留的2个静态方法:
    public static string Intern(string str);
    当str位于作为key位于CLR的驻留池时,返回对str的引用,否则将str字符串添加到hash table中,作为key,并返回引用。

    public static string IsInterned(string str);
    当str位于作为key位于CLR的驻留池时,返回对str的引用,否则返回null引用,也不添加到hash table中。

    □ 字符串驻留是进程级的

    可以跨应用程序域AppDomain而存在,驻留池在CLR加载时创建,分配在System Domain中,被进程所有AppDomain所共享,其生命周期不受GC控制。

    例子1:

            static void Main(string[] args) 
            { 
                string strA = "ab"; 
                string strB = "ab"; 
                Console.WriteLine(ReferenceEquals(strA,strB)); 
                string strC = "a"; 
                string strD = strC + "b"; 
                Console.WriteLine(ReferenceEquals(strA, strD)); 
                strD = String.Intern(strD); 
                Console.WriteLine(ReferenceEquals(strA,strD)); 
                Console.ReadKey(); 
            }

    返回:
    true
    false
    true

    分析:
    ● strA与strB内容相同,在hash table中的key相同,对应了一样的引用地址,所以返回true。
    ● strD的内容虽然与strA相同,但由于是动态生成的,不会把hash table中key为ab的引用地址赋值给strD,所以strA与strD引用地址不一样,返回false。
    ● strD = String.Intern(strD);手动对strD实施驻留,并发现hash table中已经有了ab这个key,就把对应的引用地址赋值给了strD,这样,strA与strD引用地址相同,返回true。


    例子2:

            static void Main(string[] args) 
            { 
                string s1 = "abc"; 
                string s2 = "ab"; 
                string s3 = s2 + "c"; 
                Console.WriteLine(string.IsInterned(s3) ?? "null"); 
                Console.WriteLine(ReferenceEquals(s1,s3)); 
                Console.ReadKey(); 
            }

    返回:
    abc
    false   

    分析:
    ● string.IsInterned(s3)对s3进行手动驻留,发现hash table中abc这个key,于是,就返回abc的引用地址。但并没有把引用地址赋值给s3。
    ● s1和s3的引用地址还是不一样,返回false。

    例子3:

            static void Main(string[] args) 
            { 
                string strA = "abcdef"; 
                string strC = "abc"; 
                string strD = strC + "def"; 
                Console.WriteLine(ReferenceEquals(strA,strD)); 
                string StrE = "abc" + "def"; 
                Console.WriteLine(ReferenceEquals(strA,StrE)); 
                Console.ReadKey(); 
            }

    返回:
    False,       
    true

    分析:
    ● 因为strD = strC + "def"中strD的内容虽然与strA想同,但因为是动态生成的,不会被添加到hash table中,所以引用地址不一样,返回false。
    ●  "abc"+"def在IL中呈现为abcdef,不是动态生成的,并且发现hash table中已经有了abcdef这个key,于是就把对应的引用地址赋值给了strE,这样strA和StrE就有了相同的引用地址,返回true。如图:

    1

    例子4:

            static void Main(string[] args) 
            { 
                string s = "abc"; 
                //IsInterned()获取字符串变量的引用 
                Console.WriteLine(string.IsInterned(s) ?? "null"); 
                Console.ReadKey(); 
            }

    返回"abc"。
    分析:通过string s = "abc"使得hash table中有abc这个key,当进行string.IsInterned(s)手动驻留判断的时候,发现有abc这个key,就把对应的引用地址返回。

    例子5:

        class Program 
        { 
            static void Main(string[] args) 
            { 
                string s = "ab"; 
                s += "c"; 
                //IsInterned()获取字符串变量的引用 
                Console.WriteLine(string.IsInterned(s) ?? "null"); 
                Console.ReadKey(); 
            } 
        }

    返回null
    分析:通过string s = "ab";使得hash table中有了ab这个key,s += "c"是动态拼接,不会把abc放到hashtable中,所以当通过string.IsInterned(s)手动驻留判断的时候,发现没有abc这个key,就返回null。


    参考资料
    《.NET之美》--张子阳,感谢写了这么好的书!
    《你必须知道的.NET》--王涛worktile创始人,感谢!    

  • 相关阅读:
    POJ 2400 Supervisor, Supervisee(KM)
    HDU 1695 GCD (容斥原理+欧拉函数)
    URAL 1244. Gentlemen (DP)
    HDU 1573 X问题 (中国剩余定理)
    ZOJ 3757 Alice and Bob and Cue Sports(模拟)
    HDU 1452 Happy 2004(因子和的积性函数)
    第五届山东省省赛总结
    mybatis错误——java.io.IOException: Could not find resource com/xxx/xxxMapper.xml
    springMVC+spring+mybatis搭建最近
    Log4j配置详解
  • 原文地址:https://www.cnblogs.com/darrenji/p/3599550.html
Copyright © 2020-2023  润新知