C# 字符串不可变性 + 字符串驻留池原理

一、字符串不可变性(String Immutability)

1. 定义

C# 中 string 是不可变(只读)引用类型 :一旦字符串在内存中创建,就永远不能被修改 ,任何 "修改" 操作都不会改动原字符串 ,而是新建一个字符串。

2. 为什么不可变
  1. 线程安全:只读,多线程同时读不用加锁

  2. 支持驻留池:相同文本复用同一块内存,不怕被篡改

  3. 简化 GC、缓存、哈希设计

    cs 复制代码
    string s = "abc";
    s.ToUpper();
    Console.WriteLine(s); // 还是 abc,原字符串没变

    ToUpper() 没有改原 s ,而是返回了一个全新字符串对象。

再看拼接:

cs 复制代码
string a = "123";
a += "456";

底层:

  • 不修改原 "123"
  • 重新分配内存,创建 "123456" 新字符串
  • a 引用指向新对象,旧对象等待 GC
3. 不可变带来的问题

频繁拼接字符串(循环里 +=)会不断生成新字符串、大量创建临时对象、触发 GC 。解决方案:StringBuilder StringBuilder 是可变的,内部维护字符缓冲区,原地修改,不频繁新建对象。

二、字符串驻留池(String Intern Pool)

1. 是什么

CLR 维护的一个全局字符串缓存池 ,目的:复用相同内容的字符串实例,节约内存、减少重复分配。

核心规则:内容相同的字符串,在驻留池中只存一份,多个引用指向同一个内存地址。

2. 驻留池分类
  1. 编译期驻留(常量字符串)
  2. 运行期手动驻留(string.Intern())

三、编译期驻留

原理

代码里双引号直接写的字面量字符串,编译时 CLR 会:

示例证明地址相同

cs 复制代码
string s1 = "hello";
string s2 = "hello";

// 值相等
Console.WriteLine(s1 == s2);      
// 引用地址也相等 同一个对象
Console.WriteLine(object.ReferenceEquals(s1, s2)); 

输出都是 True👉 s1、s2 指向堆上同一个字符串实例。

不进入驻留池的情况

运行时动态拼接、new 出来的字符串,默认不驻留:

cs 复制代码
string s1 = "hello";
string s2 = "hel" + "lo";   // 编译器优化,还是字面量,会驻留
string s3 = new string("hello".ToCharArray()); 

Console.WriteLine(object.ReferenceEquals(s1, s3)); // False

s3 是运行时构造,不在驻留池,是新对象。

四、运行期手动驻留:string.Intern ()

用法

cs 复制代码
string s3 = new string("hello".ToCharArray());
string internStr = string.Intern(s3);

// 现在和 s1 指向同一个驻留池实例
Console.WriteLine(object.ReferenceEquals(s1, internStr)); // True

Intern 原理

  1. 拿字符串内容去驻留池查找
  2. 找到:返回池里已有实例引用
  3. 没找到:把当前字符串加入驻留池,返回引用

适用场景:大量重复动态字符串(如日志、解析文本),手动驻留省内存。

五、驻留池存在哪里

  • .NET Framework:进程级全局驻留池,程序运行一直存在
  • .NET Core/.NET 5+:每个 AppDomain 独立驻留池 驻留池里的字符串生命周期很长,不容易被 GC 回收。

六、不可变性 + 驻留池 关联关系

  • 正因为字符串不可变,才敢做驻留池
  • 如果字符串可修改,一个引用改了内容,所有指向它的变量都会被篡改,完全乱套
  • 不可变 + 驻留池 = 安全复用内存

七、总结

  • 字符串不可改,一改建新串
  • 字面量进驻留池,同内容共用一块内存
  • 动态拼接默认不驻留,地址不同
  • string.Intern 手动入池,复用实例省内存
  • 频繁拼接用 StringBuilder,避免大量生成新字符串
相关推荐
波力海苔夹心脆6751 天前
C# WPF 资源字典详解:ResourceDictionary、MergedDictionaries 与自定义控件案例
开发语言·c#·wpf·入门教程·resource·资源字典
AI导出鸭PC端1 天前
Perplexity批量导出没有PDF模式?AI导出鸭插件补上最后一块拼图
人工智能·pdf·c#·ai导出鸭
Risehuxyc1 天前
C# 选用更流行的语言版本
c#
淡海水2 天前
09-04-内存-Unity原生对象生命周期
unity·c#·游戏引擎·gc
吴可可1232 天前
富怡CAD二次开发指南
c#
1-1=02 天前
C# IO和序列化
c#·.net·.netcore
寂寞的船2 天前
【Unity Bug挑战】第10期 SerializeReference 内存泄漏之谜
unity·c#·游戏引擎·bug
CSharp精选营2 天前
从控制台到「会动手」的 AI:用 Semantic Kernel 搭应用(小白教程)
c#·.net·ai agent·semantickernel·functioncalling
宝桥南山3 天前
.NET 11 - 尝试一下Blazor中的新特性SupplyParameterFromTempData
microsoft·微软·c#·asp.net·.net·.netcore
1-1=03 天前
C# 中 Lambda、Linq 与 Expression
c#·linq