首先,这两种方法都可以创建一个字符串,我们要分析一下到底有什么区别。

先来看一段C#程序:

代码
static void Main(string[] args)
{

var s
= new string('b', 10);
var s2
= "aaaaaaaaaa";
var s3
= "aaaaaaaaaa";
var s4
= "bbbbbbbbbb";

Console.WriteLine(
string.ReferenceEquals(s,s4));
Console.WriteLine(
string.ReferenceEquals(s2,s3));
Console.ReadKey(
true);
}

执行结果为, Flase  True

说明s2 和 s3 引用的是同一个字符转, 但是s 和 s5 并没有引用同一个字符串(区别出来了)。 来点实质的,上工具-〉Windbg, Main设置断点,执行

继续执行程序到 var s = new string('b', 10) 处 -〉 g 000007ff`001a0174(第一个蓝色框下面一条指令处)。

    1. 这条指令的作用是创建字符串 s, 返回s 的地址到rax中。 在寄存器中,我们看到rax=2882c10, 对应的是右边内存视图中的篮圈,这里我们可以看出,这个篮圈就是new出来的字符串s 在内存中的位置。(想明白了就继续……)

    2. 程序中的  第一个篮框,第二个红框,第三个红框,第四个绿框,分别对应 s, s2, s3, s4, 在程序中我们看到,s2,s3,s4的赋值都是通过一个地址间接取得的,比如s2,(mov rax,127D3050h; mov     rax,qword ptr [rax]),那我们就来看一下,这个间接地址里到底是什么,见图中第一个内存视图,127D3050h = 02882bb0。(这个地址好熟悉。。), 就是下面那个内存视图中红圈对应的数据,看得出来是我们定义的s2。

    3. s3的创建跟s2完全一样,并没有重新开辟一块空间,而是直接饮用了s2中字符串的地址,即s2 和 s3 引用同一个字符串。

    4. 下面来看,s4是如何创建的,分析过程跟s2一样,图中的绿色标志就是s4。从图中看得出来,s4并没有引用s,而是重新分配了一块内存,而且值得注意的是,s4 是先于 s 创建的。(当然,这个跟.net的编译器有关系,在此不做过多讨论。)

 

在.net中,字符串处理过程有个东西叫做"拘留池(Intern Pool)",这是.net字符串处理的一种机制,可以有效的减少字符串处理过程中的频繁内存分配,因为.net中字符串是"固定的",不可修改的,每次字符串的修改都会导致内存重新分配,而且原来的内存也不会立即回收(回收与否取决于GC,因为有可能存在其他的引用)。如果用new来新建一个字符串,必然导致系统会重新开辟一块空间(从处理过程看得出,.net编译器并没有对new进行多余的处理,而是在程序执行过程中创建的)。而非new的字符串,会先在拘留池中查找,有无相同的,如果有直接返回地址,没有的话则分配一块内存,并且在拘留池中保存新建字符串的内存位置。

所以,在我们上面那个例子当中,127D3050H貌似就是程序中的拘留池。

再来看一段程序:

代码
static void Main(string[] args)
{

var s
= new string('b', 10);
var s2
= "aaaaaaaaaa";
var s3
= "aaaaaaaaaa";
var s4
= "bbbbbbbbbb";
var s5
= string.Intern(s);

Console.WriteLine(
string.ReferenceEquals(s,s4));
Console.WriteLine(
string.ReferenceEquals(s2,s3));
Console.WriteLine(
string.ReferenceEquals(s4,s5));
Console.ReadKey(
true);
}

 

 

执行结果是:Flase, True, True

windbg的图就不上了,基本跟上面差不多。

s4 和 s5 竟然引用的同一个字符串,这里需要注意的是string.Intern(S)这个函数,

 

public String intern()


返回字符串对象的规范化表示形式。
一个初始时为空的字符串池,它由类 String 私有地维护。
当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(该对象由 equals(Object) 方法确定),
则返回池中的字符串。否则,将此 String 对象添加到池中,并且返回此 String 对象的引用。
它遵循对于任何两个字符串 s 和 t,当且仅当 s.equals(t) 为 true 时,
s.intern() == t.intern() 才为 true

所以程序执行过程中,s4创建的时候,会首先在拘留池中保存一个"bbbbbbbbbb"的地址,s5创建的时候会先在拘留池中查找有没有跟 s “相等”的字符串(这里的相等只是值的相同),如果没有会重新分配内存,在拘留池中保存地址;如果找到,则直接返回改地址。所以上例中,s5 != s, 而是和 s4 引用同一个地址的字符串。

 

 

OVER!。。写文章真累。

版面的问题,图片重新调整。。有点乱~!

 

 

Thanks

Jeremy Ma

2010-01-27

posted on 2010-01-14 11:14  Jeremy Ma  阅读(565)  评论(1)    收藏  举报