C# Intern Pool

2023-10-24 13:59:31 浏览数 (2)

1.概要

驻留池(Intern Pool)是一个用于存储字符串的内部数据结构。这个概念是由.NET提供的一项优化,主要用于减少系统对内存的使用。

当你创建一个字符串实例时,.NET会检查驻留池以查看该字符串是否已经存在。如果已存在,那么不会创建新的字符串实例,而是复用已经存在的那个。这样可以节省内存,因为相同的字符串只需要在内存中存储一次。

例如:

代码语言:javascript复制
string a = "test";
string b = "test";

在这个例子中,尽管你创建了两个字符串变量 ab,但是实际上它们引用的是同一个字符串实例 test

然而,这个特性并不适用于所有的字符串创建方式。仅当你直接赋值一个字符串字面量时,才会启用驻留池机制。如果你通过其他方式创建字符串,比如字符串连接或者通过 StringBuilder 类,那么即使字符串的内容相同,也会创建新的字符串实例。

你可以通过调用 String.Intern() 方法强制将一个字符串添加到驻留池:

代码语言:javascript复制
string c = new StringBuilder().Append("test").ToString();  // 创建一个新的字符串实例
string d = String.Intern(c);  // 强制将字符串添加到驻留池,这样 'd' 和 'a' 引用的其实是同一个字符串实例

此时,cd 是内容相同的字符串,但是 d 被添加到了驻留池,所以 abd 实际上都引用的是驻留池中的同一个字符串实例。

请注意,过度使用 String.Intern() 可能会导致内存问题,因为一旦字符串被添加到驻留池,就无法被垃圾收集器回收,除非应用程序结束。所以在大多数情况下,你应当让.NET自己管理驻留池。

使用场景

  1. 字符串的重复使用:当需要频繁使用相同的字符串时,驻留池可以大幅度地减少内存占用。例如,在解析XML或HTML文件时,元素和属性名称可能会大量重复。在这种情况下,使用驻留池可以显著节约内存。
  2. 字符串比较:由于驻留池保证了具有相同内容的字符串共享同一个实例,因此你可以通过简单地比较两个字符串的引用来判断它们是否相等,而不需要逐个字符进行比较。这能够提高字符串比较的效率。
  3. 静态和常量字符串:由于编译器自动将所有的字符串字面量添加到驻留池,因此静态和常量字符串总是被驻留。这意味着,如果你的代码中包含了大量的静态或常量字符串,那么它们实际上只占用了一份内存空间。

优点

  1. 内存优化:通过重用相同的字符串实例,驻留池可以有效地减少内存使用。这在处理大量重复字符串时特别有用,例如,在解析XML或HTML文档时。
  2. 提高性能:驻留池使得引用相同字符串的对象实际上指向同一个字符串实例。这意味着我们可以通过简单地比较两个字符串的引用来判断它们是否相等,而不需要逐字符进行比较。这可以提高字符串比较的效率。
  3. 支持字符串常量和静态字符串:.NET的编译器自动将所有的字符串字面量添加到驻留池。因此,对于常量和静态字符串,即使你在代码中多次使用了相同的字符串,它们也只会占用一份内存空间。

缺点

.NET的驻留池是固定的。一旦字符串被添加到驻留池,就不能从中移除。这意味着驻留在池中的字符串实例将会在应用程序的整个生命周期内保持存在。

.NET运行时使用驻留池来存储字面量和静态字符串,并可以选择性地存储其他字符串。由于这些字符串在整个应用程序生命周期内都可能需要访问,所以它们无法被移除。

这也是为什么要小心使用String.Intern()方法的原因之一。过度使用这个方法会导致大量的字符串被驻留,从而消耗大量的内存,而这部分内存在应用程序运行期间无法被回收。通常,你应该让.NET自己管理驻留池,只有在特定情况下(比如需要频繁比较或共享大量的字符串)才考虑手动驻留字符串。

2.详细内容

.NET的驻留池是自动管理的。当你使用字符串字面量(比如 "example")创建一个字符串时,.NET会自动将它放入驻留池。如果你再次使用相同的字符串字面量,那么.NET会直接从驻留池中获取已存在的字符串实例,而不会创建新的。

你也可以手动管理驻留池。System.String类提供了两个方法来处理驻留池:Intern()IsInterned()

  • Intern(string str):此方法用于将一个字符串添加到驻留池。如果驻留池已经包含了这个字符串,那么该方法返回对应的字符串实例;否则,它会将这个字符串添加到驻留池,并返回新添加的字符串实例。
  • IsInterned(string str):此方法用于检查一个字符串是否在驻留池中。如果驻留池包含该字符串,那么会返回对应的字符串实例;否则,返回null。

例如:

代码语言:javascript复制
string str1 = "Hello, World!";
string str2 = String.Intern(str1);
bool areEqual = Object.ReferenceEquals(str1, str2);  // 返回true,因为str1和str2引用了相同的字符串实例

str1 = new StringBuilder().Append("Hello, ").Append("World!").ToString();  // 创建一个新的字符串实例
str2 = String.IsInterned(str1);  
areEqual = Object.ReferenceEquals(str1, str2);  // 返回false,因为str1并没有被添加到驻留池

0 人点赞