|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
一个很大的类库。应用程序之所以难以跨平台,在于直接调用了特定平台的接口,而一个巨大的类库,就能极大地减少应用程序对平台的依赖。系列文章目次索引:《.NET,你健忘了么》
一.文章伊始
在文章之前,说下写出这篇文章的目标。在我今天的一篇文章<<重温计划形式(一)——享元形式>>中,我在文中提到了关于String的字符串驻留机制。在文章的批评中,杨同砚对我的字符串相干概念提出质疑,而且成文,不外我如今没法找到谁人链接了。
因而,我想把这个老失落牙的话题在此重谈。
事实我们对String这个经常使用的范例有几了解。
二.从C看起
C言语是我打仗的第一个程序言语。还记妥当时给我的C言语先生是一个专业做JavaSOA的先生。
因而,她在教学C的时分常常给我们时不时地与Java做着对照,只管我们事先其实不懂Java是个甚么东东,只晓得这个词常常呈现于手机游戏上。
事先我还记得先生一句很典范的话:我们要记得,C中没有字符串这个观点(实在我们事先还不懂甚么是字符串),所谓的字符串在C中体现为字符数组。
那就让我们来温习一下,在C中的“字符串”的体现情势:
chars[]=”abc”;
接上去,我们即可以利用s往挪用各类“字符串”函数。
那末我们能够分明地看到在C言语中,“字符串”实在存储的就是字符数组的首地点,那末在.NET中又是怎样呢?
三.Stringvsstring
在黉舍的时分,这个成绩被同砚有数次问过,特别是良多学Java的伴侣。
string实在就是String的别号,当两者编译为IL代码时,两者并没有区分,正如int之于System.Int32。
两者的分离仅仅在于:
1.string是C#言语的基元范例,看起来更C#。
2.System.String是FCL的基元范例。
我经常是如许来利用:
1.假如触及到言语的互操纵,那末毋容置疑,必定是System.String,不再赘述,若有成绩,请参考《.NET,你健忘了么(一)——服从CLS》
2.假如只是声明一段字符串,我会利用string,看上往可读性更高,相似于你会利用inti=3;而很少见到System.Int32i=3一样。
3.假如是触及到利用字符串的静态办法,那末我经常利用System.String,由于String看起来更像一个类。
四.字符串的稳定形式
我们在这里先看例子:- staticvoidMain(string[]args){strings1="Hello";strings2="Hello";Console.WriteLine(Object.ReferenceEquals(s1,s2));s2="Helloworld";Console.WriteLine(Object.ReferenceEquals(s1,s2));}
复制代码 了局呢?
<br>
那为何s1和s2第一次的援用明显相称,但是经由一次改动却又分歧了呢?
那就让我们来发表个中的奥密。
五.深切字符串驻留
我们起首要先懂得.NET的运转历程,假如还不太懂得,请参考我的<<剖析.NET运转全程>>。
在CLR被加载以后,便SystemDomain所对应的托管堆中初始化了一个HashTable。这个HashTable的目标就是为了存储我们所创立过的字符串。
在Hashtable中,Key是string的内容,Value是这个字符串所对应的内存地点。
那我们来剖析下下面的代码,实在历程以下:
strings1=”Hello”;
strings2=”Hello”;
<br>
如今的s1和s2是指向统一块地点,然后我们改动了s2的值,假定s2=”World”;那末这个时分:
<br>
固然,这个时分s1和s2就不指向统一块援用地点了。
好,如今让我们来体系的剖析一下这个历程。
现在始化一个字符串时,会起首在这个体系初始化的Hashtable中查找每个字符串常量在Hashtable中是不是存在,
假如不存在,那末他便起首在托管堆平分配一块内存地点来存储这个字符串常量,然后在Hashtable中增添一个键值对,将字符串的内容存储为Key,而将分派的内存地点存储为Value。
假如存在,那末就在将该援用指向原本的地点。上面的代码进一步印证了这个概念:- staticvoidMain(string[]args){strings1="Hello";strings2="Hello";strings3="Helloworld";Console.WriteLine("Compares1ands2:"+Object.ReferenceEquals(s1,s2));s2="Helloworld";Console.WriteLine("Compares1ands2:"+Object.ReferenceEquals(s1,s2));Console.WriteLine("Compares2ands3:"+Object.ReferenceEquals(s2,s3));}
复制代码
<br>
那末当改动该字符串值的时分又产生了甚么?这就是稳定形式的精华,我们也称字符串横定性。
六.深切字符串恒定性
字符串横定性是指一个字符串一经创立,就不成改动。那末也就是说当我们改动string值的时分,便会在托管堆上从头分派一块新的内存空间,而不会影响到原本的内存地点上所存储的值。
实在这也就是为何说字符串是特别的援用范例的缘故原由。他有着值范例的特性,也有着援用范例的特性。
微软之以是如许计划我以为是出于两点:
1.坚持字符串的恒定性意味着多用户共享统一块字符串地点时不会呈现线程同步的成绩。
2.假如没有了字符串恒定,那末字符串的驻留基础也就无从完成了。
七.寻找计划根源
微软为何要如许往计划字符串的布局呢?
我想这也是杨同砚对我的概念提出质疑的基本缘故原由。
固然,实质固然是为了节俭内存。至于事实如许的内存节俭会有几。我从上面的例子来讲明。
我们做一个Web程序,不计其数的用户往会见统一台服务器,String作为最经常使用的范例固然被频仍利用,这毋容置疑。那末在服务器中,统一个字符串便可能被初始化不计其数,我们再进步用户量,那末大概就是百万级的数目,那末这对服务器的内存是个何等年夜的占用。
那末,有了字符串驻留,假如两个字符串值相称的时分,就把两个字符串指向统一块对象,那末如许是否是节俭了相称多的内存呢?
至于杨同砚的别的一个概念,说岂非在全部运转过程当中这个Hashtable都存在,而且保留着一切的数据么?
我以为这个是很简单举行辩驳的。很分明,Hashtable在全部运转历程都存在的,而数据,我团体以为应当是有两种大概:
1.按期往清算失落无用的键值对。
2.像渣滓接纳一样,只要当存储的字符串凌驾了Hashtable的巨细,大概对定位Key的效力形成必定影响时便对其举行接纳。
八.String在IL上的挺拔独行
我们都晓得,在机关一个援用对象时,对应的IL代码是newobj。
可是.NET为string筹办了特别的声明体例:ldstr。该指令经由过程元数据中取得的文本常量来机关字符串对象。
增补一点,为何说是经由过程元数据中取得的文本常量呢?由于C#将String作为了本人的基元范例,因而编译器会将这些文本常量寄存在托管模块的元数据中。
不管怎样,这申明了String是个特别的范例,CLR为他筹办了更高效,挺拔独行的体例。
九.String事实是不是享元?
String经由过程一个Hashtable来为本人保留一个缓存,然后每次初始化一个字符串时都先经由过程搜刮缓存找到是不是存在能够重用的对象,然落后行重用大概创立新的实例。
而享元形式是接纳共享手艺办理大批细粒度对象的爆炸成绩。
是否是一样呢?
十.Intern办法
让我们起首看看MSDN对Intern的注释:
Intern:检索体系对指定String的援用。假如存在如许的字符串,那末便前往他的援用,不然便创立一个新的字符串,然后前往他的援用。
我为何在String那末多的办法中单单钟情于这个办法呢?让我们先来看这段代码:- staticvoidMain(string[]args){strings1="Hello";strings2="HelloWorld";strings3=s1+"World";Console.WriteLine(Object.ReferenceEquals(s2,s3));}
复制代码- [/code]
- <br>
- 这也是事先杨同砚辩驳我的一个实际根据。
- 缘故原由就是在于s3是静态天生的字符串,如许的字符串是不会增加到缓存哈希表中举行保护到的。
- 那末这个时分Intern就派上用处了。
- 让我们来改写一下下面的代码:
- [code]staticvoidMain(string[]args){strings1="Hello";strings2="HelloWorld";strings3=s1+"World";Console.WriteLine(Object.ReferenceEquals(s2,s3));s3=String.Intern(s3);Console.WriteLine(Object.ReferenceEquals(s2,s3));}
复制代码
<br>
由于Intern的感化是搜刮全部Hashtable,然后往找是不是有这个字符串的相干援用。因而他找到了以后便会将这个援用前往给s3,那末这个时分,固然s2和s3的援用便相称了。
而这个办法的完成也让我们来看下:- publicstaticstringIntern(stringstr){if(str==null){thrownewArgumentNullException("str");}returnThread.GetDomain().GetOrInternString(str);}
复制代码 这个办法事实在实践中有甚么用呢?让我们向下看。
十一.学乃至用,虫篆之技
我们在实践的使用中,字符串对照有着很年夜的使用,String.Compare()。
这个办法的实质是将全部string拆开,然后对照个中的每一个字符。也就是在口试题中经常碰到的,我最常说的一句话就是:把字符串当做字符数组玩!
可是,我们晓得,如许是很消耗效力的。因而我们能够依据字符串的驻留特征往想一想其他办法。
在上文中,我频仍的往利用Object.ReferenceEquals()。望文生义,这个办法就是对照两个字符串的内存地点是不是相称。因为字符串的驻留手艺,因而两个相称的字符串所指向的地点是相称的。
因而我们可使用Object.ReferenceEquals()来进步我们字符串对照的效力。
固然,极可能会呈现某一字符串是静态天生的情形,那末这个时分Intern就派上用处了,固然,Intern一样是个泯灭效力的办法,因而假如我们只必要举行大批的对照操纵,就没有需要利用这个办法了。
别的,就是说因为字符串的恒定稳定性,我们在字符串拼接时,实践上就发生了大批的内存渣滓。因而,假如要大批的字符串拼接,要利用StringBuilder类来完成,这个良多人都晓得,我就不具体说这个了。
十二.要点总结
String是个特别的援用对象。
关头就在于他的字符串驻留手艺和字符串的恒定稳定性。
不早了,睡一觉还要爬起来下班。感谢人人的存眷。假如有甚么分歧的定见,接待人人留言会商。我一向以为手艺是会商出来的,而不是本人闷在家里想出的!
参考文章:《你所必需晓得的.NET》
《.NET框架程序计划》
《深切了解.NET》
本文来自:http://www.ckuyun.com/xinyuperfect/archive/2009/03/30/1424753.html
如果英语好,口才好,加上女孩子的优势说不定有机会进去做做别的工具) |
|