C# 内存机制详解:值类型、引用类型与 String 的不可变性


C# 内存机制详解:值类型、引用类型与 String 的不可变性

1. 值类型 (Value Type)

代表类型: int, bool, struct, double, char 等。

特点

  • 存储位置: 变量名和实际数据都保存在 栈 (Stack) 中。
  • 赋值行为: 直接拷贝一份完整的数据。修改其中一个,另一个不受影响。

代码示例

csharp 复制代码
int a = 10;
int b = a; // 拷贝具体的值
b = 20;    // 修改 b,a 依然是 10

内存图解

text 复制代码
栈 (Stack)                 堆 (Heap)
+----------+----------+   +-------------------+
| 变量名    |   数据   |   |                   |
+----------+----------+   |                   |
|    a     |    10    |   |  (值类型不占用堆)   |
+----------+----------+   |                   |
|    b     |    20    |   |                   |
+----------+----------+   +-------------------+

2. 引用类型 (Reference Type)

代表类型: class, interface, array, delegate 等。

特点

  • 存储位置: 栈中存的是 内存地址 (指针) ,实际的对象内容存在 堆 (Heap) 中。
  • 赋值行为: 拷贝的是地址。两个变量指向同一个堆对象。

代码示例

csharp 复制代码
Person p1 = new Person { Name = "Alice" };
Person p2 = p1;  // 拷贝地址,两人指向同一个对象
p2.Name = "Bob"; // 通过 p2 修改堆内容,p1.Name 也会变成 "Bob"

内存图解

text 复制代码
栈 (Stack)                          堆 (Heap)
+----------+-----------+           +--------------------------+
| 变量名    |  内存地址  |           |        对象实际数据        |
+----------+-----------+           +--------------------------+
|   p1     |  [0x001]  | --------> | 地址[0x001]: {Name:"Bob"} |
+----------+-----------+      /    +--------------------------+
|   p2     |  [0x001]  | ----/     (p1和p2共享同一块堆空间)
+----------+-----------+

3. String 的特殊性:不可变的引用类型

string 是引用类型,但在行为上表现得像值类型,这就是因为它的 不可变性 (Immutability)。

核心规则

一旦在堆上创建了字符串对象,它的内容就永远不能被修改。 所有的"修改"操作实际上都是在堆中创建了一个全新的字符串对象,并让变量指向新地址。

代码演示与内存步解

csharp 复制代码
string s1 = "Hi";
string s2 = s1; // s2 指向与 s1 相同的地址
s2 = "Yo";      // 并不是修改 "Hi",而是新开辟空间存 "Yo"
第一步:执行 s1 = "Hi"; s2 = s1;
text 复制代码
栈 (Stack)                          堆 (Heap)
+----------+-----------+           +---------------------+
| 变量名    |  内存地址  |           |      字符串对象      |
+----------+-----------+           +---------------------+
|   s1     |  [0xAAA]  | --------> | [0xAAA]: "Hi"       |
+----------+-----------+      /    +---------------------+
|   s2     |  [0xAAA]  | ----/
+----------+-----------+
第二步:执行 s2 = "Yo";
text 复制代码
栈 (Stack)                          堆 (Heap)
+----------+-----------+           +---------------------+
| 变量名    |  内存地址  |           |      字符串对象      |
+----------+-----------+           +---------------------+
|   s1     |  [0xAAA]  | --------> | [0xAAA]: "Hi"       |
+----------+-----------+           +---------------------+
|   s2     |  [0xBBB]  | --------> | [0xBBB]: "Yo"       |
+----------+-----------+           +---------------------+

4. 总结对比表

特性 值类型 (Value Type) 引用类型 (Reference Type) String (特殊引用类型)
存储位置 栈 (Stack) 栈(地址) + 堆(对象) 栈(地址) + 堆(对象)
赋值行为 拷贝具体数值 拷贝内存地址 拷贝内存地址
修改行为 原地修改变量值 原地修改堆中的对象内容 禁止原地修改,创建新对象并更向新地址
典型代表 int, struct class, array string

为什么 String 要这样设计?

  1. 性能与内存: 支持"字符串驻留",相同内容的字符串在内存中只存一份。
  2. 安全: 字符串作为参数传递(如文件路径)时,不会在途中被意外修改。
  3. 线程安全: 多个线程同时读一个不可变对象,不需要加锁。

提示:在进行大量字符串拼接操作时,请务必使用 StringBuilder 以避免在堆中产生大量无用的中间字符串碎片。

相关推荐
专业程序开发源5 分钟前
springboot校企合作双选会一站式预约与面试系统16010-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·面试·php·课程设计
郑州光合科技余经理1 小时前
同城商城源码:商品库和订单库怎么随包交付
android·java·开发语言·后端·uni-app·php·ai编程
\光辉岁月/1 小时前
4.String类
java·面向对象·stringbuilder·string·stringbuffer·字符串类
蚰蜒螟1 小时前
从 Java SocketChannel 到 TCP 挥手:一次 close() 的完整调用链与引用计数解析
java·python·tcp/ip
damoluomu1 小时前
国产 Java CMS 的协议和坑,讲清楚
java·开发语言
Wang's Blog1 小时前
Java 项目实战: 交友项目 - 黑名单:分页查询黑名单
java·开发语言·交友
蚰蜒螟1 小时前
从 Java write 到磁盘扇区:一次写入操作的完整内核穿越之旅
java·开发语言
en.en..2 小时前
volatile 关键字整理(嵌入式 C 标准)
java·开发语言
captain3762 小时前
Spring IoC和DI(2)
java·后端·spring