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的背后发生了什么

展开 class Program
{
static void Main(string[] args)
{
Person p;
p = new Person(20);
}
} public class Person
{
public int _age; public Person(int age)
{
_age = age;
} public Person()
{ }
}
另外,引用类型的值,比如这里的引用类型Person中的值_age也被分配在托管堆上。

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

展开 class Program
{
static void Main(string[] args)
{
Person p1 = new Person(20);
Person p2 = p1;
Console.WriteLine("没有改变时p2的年龄是:" + p2._age + "岁");
p1._age = 18;
Console.WriteLine("改变p1的值,p2的年龄也被改变了,现在是:" + p2._age + "岁,真好,年轻了!");
Console.ReadKey();
}
} public class Person
{
public int _age; public Person(int age)
{
_age = age;
} public Person()
{ }
}

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。如图:

例子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创始人,感谢!
05引用类型以及特殊引用类型string的更多相关文章
- 值类型与引用类型(特殊的string) Typeof和GetType() 静态和非静态使用 参数传递 相关知识
学习大神博客链接: http://www.cnblogs.com/zhili/category/421637.html 一 值类型与引用类型 需要注意的string 是特殊类型的引用类型. 使用方法: ...
- js常用API 数据类型 基本类型,基本包装类型,引用类型 Object String Array Boolean Number Date Math
数据类型 变量.作用域及内存 基础类型(primitive value):Undefined.Null.Boolean.Number和String.这些类型在内存中分别占用固定大小的空间,他们的值保存 ...
- 转:C# String为值类型还是引用类型
关于String为值类型还是引用类型的讨论一直没有平息,最近一直在研究性能方面的问题,今天再次将此问题进行一次明确.希望能给大家带来点帮助,如果有错误请指出. 来看下面例子: //值类型 int a ...
- 图解C#的值类型,引用类型,栈,堆,ref,out
C# 的类型系统可分为两种类型,一是值类型,一是引用类型,这个每个C#程序员都了解.还有托管堆,栈,ref,out等等概念也是每个C#程序员都会接触到的概念,也是C#程序员面试经常考到的知识,随便搜搜 ...
- .NET面试题解析(01)-值类型与引用类型
系列文章目录地址: .NET面试题解析(00)-开篇来谈谈面试 & 系列文章索引 常见面试题目: 1. 值类型和引用类型的区别? 2. 结构和类的区别? 3. delegate是引用类型还 ...
- 30天C#基础巩固-----值类型/引用类型,泛型,空合并操作符(??),匿名方法
一:值类型/引用类型的区别 值类型主要包括简单类型,枚举类型,和结构体类型等,值类型的实例通常被分配在线程堆栈上面变量保存的内容是实例数据本身.引用类型被分配在托管堆上,变量保存的是地址.引 ...
- JavaScript高级程序设计学习笔记--引用类型
Object类型 对象字面量表示法: var person={ name:"Nicholas", age:29, 5:true }; 这人例子会创建一个对象,包含三个属性:name ...
- js-变量、作用域和内存问题,引用类型
变量.作用域和内存问题 1.变量可能包含两种不同数据类型的值:基本类型值以及引用类型值:引用类型值保存的是内存中的对象 2.对象是按值传递的, function setName(obj){ obj.n ...
- 深入解析js中基本数据类型与引用类型,函数参数传递的区别
ECMAScript的数据有两种类型:基本类型值和引用类型值,基本类型指的是简单的数据段,引用类型指的是可能由多个值构成的对象. Undefined.Null.Boolean.Number和Strin ...
随机推荐
- 【笔记】Python简明教程
Python简明教程,此资源位于http://woodpecker.org.cn/abyteofpython_cn/chinese/ s=u'中文字符' #u表示unicode,使用u之后能正常显示中 ...
- ZooKeeper实践:(2)配置管理
一. 前言 配置是每个程序不可或缺的一部分,配置有多重方式:xml.ini.property.database等等,从最初的单机环境到现在的分布式环境. 1. 以文件的格式存储配置,修改任何都 ...
- mongo oplog 整理
首先需要介绍一下mongodb local库的作用 local库是MongoDB的系统库,记录着时间戳和索引和复制集等信息 test:PRIMARY> use local switched to ...
- Codeforces Round #207 (Div. 1) D - Bags and Coins 构造 + bitset优化dp + 分段查找优化空间
D - Bags and Coins 思路:我们可以这样构造,最大的那个肯定是作为以一个树根,所以我们只要找到一个序列a1 + a2 + a3 .... + ak 并且ak为 所有点中最大的那个,那么 ...
- Failed to load JavaHL Library. SVN
以前使用的电脑是32位的,安装的svn可以正常使用,但是现在的电脑室64位的,安装好svn后,把项目提交到svn的过程中,总是弹出来一个错误的对话框: Failed to load JavaHL Li ...
- react篇章-React Props
state 和 props 主要的区别在于 props 是不可变的,而 state 可以根据与用户交互来改变.这就是为什么有些容器组件需要定义 state 来更新和修改数据. 而子组件只能通过 pro ...
- 把eclipse写好的web项目导入idea 部署到Tomcat
主要分为项目配置和tomcat配置两大步骤. 一.项目配置 打开idea,选择导入项 选择将要打开的项目路径后,继续选择项目的原本类型(后续引导设置会根据原本的项目类型更新成idea的项目),此例中选 ...
- 浅谈期望的线性性(可加性)【CodeForces280c】【bzoj3036】【bzoj3143】
[pixiv] https://www.pixiv.net/member_illust.php?mode=medium&illust_id=63399955 向大(hei)佬(e)势力学(di ...
- 【BZOJ】4720: [Noip2016]换教室
4720: [Noip2016]换教室 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 1690 Solved: 979[Submit][Status ...
- Remove-Invalid-Parentheses-题解
题意 Remove the minimum number of invalid parentheses in order to make the input string valid. Return ...