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 以避免在堆中产生大量无用的中间字符串碎片。

相关推荐
小Ti客栈7 小时前
Spring Boot 集成 Springdoc-OpenAPI 与 Knife4j实现接口文档与可视化调试
java·spring boot·后端
Ai拆代码的曹操8 小时前
Spring 事务 REQUIRES_NEW 嵌套调用:连接池翻倍的秘密
java·后端·spring
动恰客流统计8 小时前
ReID边缘计算视觉统计:餐饮店客流增长的数字化破局路径
java·大数据·运维·人工智能
Ivanqhz9 小时前
Rust &‘static str浅析
java·前端·javascript·rust
code bean10 小时前
【C#】 `Channel<T>` 深度解析:生产者-消费者模式的现代解法
数据结构·c#
weixin_4196583112 小时前
Docker 搭建 Jenkins 服务
java·docker·jenkins
captain37612 小时前
多线程线程安全问题
java·java-ee
CoderYanger13 小时前
A.每日一题:1979. 找出数组的最大公约数
java·程序人生·算法·leetcode·面试·职场和发展·学习方法
灵极海13 小时前
LangChain4j RAG 实战完整指南:从入门到踩坑
java·langchain
yaoxin52112313 小时前
476. Java 反射 - 调用方法
java·开发语言