`
Kingson_Wu
  • 浏览: 123690 次
文章分类
社区版块
存档分类
最新评论

C#中字符串的内存分配与驻留池

 
阅读更多

C#中字符串的内存分配与驻留池

作者:cyoooo7来源:博客园发布时间:2011-07-0516:31阅读:2246次推荐:2原文链接[收藏]

摘要:当有多个字符串变量包含了同样的字符串实际值时,CLR可能不会为它们重复地分配内存,而是让它们统统指向同一个字符串对象实例。

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

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

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

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

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

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

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

StringBuildersb=newStringBuilder();
sb.Append("He").Append("llo");

strings1="Hello";
strings2=sb.ToString();

boolsame=(object)s1==(object)s2;

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

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

StringBuildersb=newStringBuilder();
sb.Append("He").Append("llo");

strings1="Hello";
strings2=String.Intern(sb.ToString());

boolsame=(object)s1==(object)s2;

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

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

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

分享到:
评论

相关推荐

    C#字符串内存分配与驻留池学习分享

    C#字符串内存分配与驻留池是.NET框架中一种高效的内存管理策略,主要涉及到字符串对象在内存中的存储和复用。在C#中,字符串(String)是一个不可变的类型,这意味着一旦创建,其内容就不能改变。这种特性使得字符串...

    浅谈C#中的string驻留池

    首先,我们来了解一下什么是字符串驻留池。简单来说,驻留池是一种内存分配策略,允许相同内容的字符串对象在内存中只存在一份。这种策略特别适用于那些大量使用字符串字面量的情况。例如,在程序中多次出现"hello...

    C#语法字符串与数组的研究

    C#使用字符串驻留机制来优化内存使用,避免相同字符串的多次拷贝。这种机制使得相同的文本常量在内存中只会有一份拷贝。 字符串的基本操作包括: 1. **索引器**:可以通过索引访问字符串中的单个字符,如`string...

    C#中字符串的一般性和特殊性

    字符串驻留池是一种优化机制,用于存储和重用具有相同值的字符串对象。当创建一个新的字符串实例时,如果该字符串的值与已存在于驻留池中的字符串相匹配,那么新实例将直接引用池中的现有字符串,而不是创建新的内存...

    .NET再谈String

    【字符串驻留的深入】当创建一个新的字符串时,CLR会检查字符串驻留池,如果池中已经存在相同内容的字符串,那么新字符串就会共享同一块内存空间。如果不存在,就会在托管堆上分配新的内存,并将新字符串添加到驻留...

    C#中下笔记

    11. **字符串驻留池**:字符串是不可变的,C#使用字符串驻留池来优化内存使用,相同内容的字符串会共享同一个实例。`string s3 = string.Intern(s1 + s2);` 和 `string s4 = string.IsInterned(x1 + x2 + x3);` 展示...

    深入理解C#中的String

    为了优化性能,C#引入了字符串驻留池。当创建一个字符串时,系统会检查驻留池中是否已有相同内容的字符串。如果存在,就直接使用池中已有的字符串引用;若不存在,新创建的字符串会被添加到池中。这种方式减少了内存...

    C# 特殊的string类型详解

    本文将深入探讨`string`类型的特性,包括它的内存管理、字符串驻留池、以及在拼接操作中的性能优化。 首先,我们要明白`string`在C#中是如何创建的。不同于其他引用类型通常通过`newobj`指令来实例化,`string`使用...

    C# 控制台应用程序——随机数

    这是老师做过的一个项目,抽取了当中核心之一的内容给大家试试,有些难度。 1、实验目的 ...看看这里:《告诉一个不一样的.NET Framework字符串驻留》,另外也可以找找对应.NET Framework底层实现的文章。

    一些简单的代码优化原则

    相反,应使用 `StringBuilder` 或 `StringBuffer` 类来构建字符串,特别是在循环中,这可以显著减少内存分配和垃圾回收的压力。 2. **使用局部变量**:局部变量存储在栈中,访问速度快于实例或静态变量(存储在堆中...

    SNMP协议使用示例C#源文件

    在C#中,可以通过创建ManagementObject类的实例来实现,设置相应的IP地址、社区字符串(Community String,类似于认证凭证)和其他参数。 2. **OID(Object Identifier)**:OID是MIB中的唯一标识符,用于定位管理...

    C#版本口令加密BAT源码_0520.rar

    - **使用SecureString**: 在C#中,`SecureString`类用于存储敏感信息,它比普通字符串更安全,因为其内容不会驻留在内存中直到程序结束。 - **避免明文存储密码**: 任何时候都应存储加密后的密码,而不是明文。 9...

    snmp.rar_C#snmp_SNMP_hp snmp

    在使用HP SNMP++开发包时,开发者首先需要了解如何配置SNMP服务,包括设置社区字符串(用于身份验证)和选择正确的SNMP版本。然后,通过引用库文件,导入必要的命名空间,并创建SNMP类的实例来发起请求。例如,可以...

Global site tag (gtag.js) - Google Analytics