一起谈.NET技术,C#中字符串的内存分配与驻留池

news/2024/7/20 13:41:17 标签: 内存管理, c#

  刚开始学习C#的时候,就听说CLR对于String类有一种特别的内存管理机制:有时候,明明声明了两个String类的对象,但是他们偏偏却指向同一个实例。如下:


  
String s1 = " Hello " ;
String s2
= " Hello " ;
//
s2和s1的实际值都是Hello
bool same = ( object ) s1 == ( object ) s2;
//
这里比较s1、s2是否引用了同一个对象实例
// 所以不能写作bool same = s1 == s2;
// 因为String类重载了==操作符来比较String对象包含的实际值

  这里的same会被赋值为true。也就是说s1真的和s2引用了同一个String对象。当然,应该注意到的是s1和s2都被统一赋值为同一个字符串Hello,这才是出现上述情况的原因。

  现在我们初步得出结论,当有多个字符串变量包含了同样的字符串实际值时,CLR可能不会为它们重复地分配内存,而是让它们统统指向同一个字符串对象实例。(这里我说了可能,是因为某些情况下,确实也会发生同一个字符串实际值在内存中有多份副本同时存在。请继续往下看。)

  我们知道,String类有很多特别的地方,其中之一就是它是不会改变的(immutable)。这说明在我们每次对一个String对象进行操作时(比如说使用Trim,Replace等方法),并不是真的对这个String对象的实例进行修改,而是返回一个新的String对象实例作为操作执行的结果。String对象的实例一经生成,到死都不会被改变了!

  基于String类这样的特性,CLR让表示相同的字符串实际值的变量指向同一个String事例,就是完全合理的了。因为利用任何一个对String实例的引用所进行的修改操作都不会切实地影响到该实例的状态,也就不会影响到其他所有指向该实例的引用所表示的字符串实际值。CLR如此管理String类的内存分配,可以优化内存的使用情况,避免内存中包含冗余的数据。

  为了实现这个机制,CLR默默地维护了一个叫做驻留池(Intern Pool)的表。这个表记录了所有在代码中使用字面量声明的字符串实例的引用。这说明使用字面量声明的字符串会进入驻留池,而其他方式声明的字符串并不会进入,也就不会自动享受到CLR防止字符串冗余的机制的好处了。这就是我上文提到的某些情况下,确实也会发生同一个字符串实际值在内存中有多份副本同时存在的例子。请看这个例子:


  
StringBuilder sb = new StringBuilder();
sb.Append(
" He " ).Append( " llo " );

string s1 = " Hello " ;
string s2 = sb.ToString();

bool same = ( object ) s1 == ( object ) s2;

  这时same就不是true了,因为虽然s1,s2表示的是相同的字符串,但是由于s2不是通过字面量声明的,CLR在为sb.ToString()方法的返回值分配内存时,并不会到驻留池中去检查是否有值为Hello的字符串已经存在了,所以自然不会让s2指向驻留池内的对象。

  为了让编程者能够强制CLR检查驻留池,以避免冗余的字符串副本,String类的设计者提供了一个名为Intern的类方法。下面是该方法的一个示例:


  
StringBuilder sb = new StringBuilder();
sb.Append(
" He " ).Append( " llo " );

string s1 = " Hello " ;
string s2 = String.Intern(sb.ToString());

bool same = ( object ) s1 == ( object ) s2;

  好了,same又是true了。Intern方法接受一个字符串作为参数,它会在驻留池中检查是否存在参数所表示的字符串。如果存在,则返回那个驻留池中的字符串的引用;否则向驻留池中加入一个新的表示相同值的字符串,并返回这个字符串的引用。不过要注意的是,就算Intern方法在驻留池中找到了相同值的字符串,也不能让您省却一次字符串内存分配的操作,因为作为参数的字符串已经被分配了一次内存了。而使用Intern方法的好处在于,如果Intern方法在驻留池中找到了相同值的字符串,此时虽然在内存中存在两份该字符串的副本(一份是参数,一份是驻留池中的),但是随着时间的流逝,参数所引用的那个副本会被垃圾回收掉,这样对于该字符串内存中就不存在冗余了。

 

  当您的程序中存在某个方法,可以根据不同的上下文环境创建并返回一个很长的字符串,而在程序运行的过程中它有会经常返回同样的字符串时,您可能就要考虑考虑使用Intern方法来提高内存的利用率了。

 

  不过同样值得注意的是,使用Intern方法让一个字符串存活于驻留池中也有一个副作用:即使已经不存在任何其它引用指向驻留池中的字符串了,这个字符串仍然不一定会被垃圾回收掉。也就是说即使驻留池中的字符串已经没有用处了,它可能也要等到CLR终结时才被销毁。当您使用Intern方法的时候,也应该考虑到这个特殊的行为。

转载于:https://www.cnblogs.com/waw/archive/2011/09/01/2162838.html


http://www.niftyadmin.cn/n/1682827.html

相关文章

如何离开学术界(写给科学博士)

译者序:有很多人在不知道读博士(PHD)的意思的前提下(大部分PHD是为了培养大学教授的),读了工科的博士之后,发现自己其实并不想钻进故纸堆里写那些“没人看的废纸”,而是希望创造点什么&#xff…

Spring+Jpa整合的过程中遇到的一个问题。。。纠结了我半天。。。

今天在出来Spring整合Jpa,由于是刚开始整合,难免会遇到很多问题。。。。。但是今天的这个问题真的让人很郁闷。。一直提示 No PersistenceProvider specified in EntityManagerFactory configuration, and chosen PersistenceUnitInfo does not specify …

汇编期末够用系列

文章目录Chapter II通用寄存器AX(accumulator)累加器BX(base)基址变址CX(count)计数器DX(data)数据SP(stack pointer)堆栈指针BP(base pointer)基址指针DI(destination index)目的变址 & SI(source index)源变址专用寄存器IP(instruction pointer)指令指针FIAGS(program st…

2011路由知识普及和经验分享贴

转自:http://www.right.com.cn/forum/viewthread.php?tid46281&extra&page1 流行的第三方路由固件,主要有DDWRT,Tomato,Openwrt 当然还有其他一些更加小众的版本,和很多从这三大固件衍生出来的修改版固件 我的这篇文章,可以让大家对这些固件有一个相对比较…

汇编Ch.5 Ch.6笔记

Chapter V: 程序设计 躺雷记录: 关于汇编中大小写的问题: 各条指令不区分大小写但是用户定义的各个变量, 标号等, 将区分大小写 关于汇编中的中括号: 对于MOV: num dword 2 mov eax,2 mov ebx,num mov ecx,[num] ;执行完ebxecx2 ;证明了对于变量, 加不加[]得到的都是…

objective-c 源文件组织

1,将interface Class 放在.h头文件中,将implementation Class 放在.h 中。2.在将继承类中进行文件的拆分,在头文件中用使用#import 来导入头文件,而不能使用class 来导入头文件,因为在编译子类时,它需要超类…

预编译头文件及fatal error C1083: Cannot open precompiled header file:Release/***.pch

首先,什么是pch文件? pch文件是C的预编译文件头。 要想弄明白出现问题的原因还是先了解一下pch文件吧,参考一下资料: 预编译头文件(一般扩展名为.PCH),是把一个工程中较稳定的代码预先编译好放在一个文件(.PCH)里.这些预先编译…

浅谈人人网以及淘宝网的IM即时通信以及point-to-point通信

我想,淘宝网或者是人人网,应该是大家较为熟知的网站了。就算你不是它们的使用者,我想你也应该早有耳闻。人人网和淘宝网的右下角,都提供了"在线"通信功能: 这次,我就和大家来谈谈我认为的这些网站…