查看: 76|回复: 0

C# 内存管理核心:内存基础、GC、IDisposable、using 模式

[复制链接]

2

主题

0

回帖

0

积分

积极分子

金币
0
阅读权限
220
精华
0
威望
0
贡献
0
在线时间
0 小时
注册时间
2011-1-30
发表于 2026-4-24 13:40:00 | 显示全部楼层 |阅读模式

一、内存基础:栈 vs 堆

 
C# 把内存分成两块核心区域,分配规则完全不同,这是理解 GC 的前提。
 

1. 栈内存 (Stack)

 
  • 存放内容:值类型(int、bool、struct)、引用类型变量的引用地址
  • 特点:
    • 自动分配、自动释放(方法执行完立刻释放)
    • 速度极快,无需 GC 管理
    • 大小固定,空间小
     
  • 生命周期:跟随方法 / 代码块,执行结束立即回收
 

2. 托管堆 (Heap)

 
  • 存放内容:引用类型(string、class、数组、委托)
  • 特点:
    • 动态分配,空间大
    • 手动不释放,由 GC(垃圾回收器)自动管理
     
  • 生命周期:没有引用指向它时,GC 才会回收
 

总结

 
int a = 10;        // 栈:方法结束自动没了
string s = "abc";  // 栈存引用地址,堆存真实数据:GC 负责回收
 
 

 

二、GC 常识:垃圾回收机制

 
GC = Garbage Collector,C# 的自动内存管家,专门清理托管堆上没用的对象。
 

1. GC 做什么?

 
  • 找到堆上没有任何引用的无用对象
  • 释放它们的内存
  • 压缩内存(让存活对象紧凑排列,减少内存碎片)
 

2. GC 什么时候工作?

 
  • 新对象分配,堆空间不足时(主动触发)
  • 系统内存紧张时
  • 手动调用 GC.Collect()(不推荐,会影响性能)
 

3. GC 关键特点

 
  1. 只清理托管资源
     
    托管资源 = 堆上的对象(string、class 等)
  2. 不清理非托管资源
     
    非托管资源 = 文件句柄、数据库连接、网络套接字、图片流、窗口句柄
     
    → 这些必须手动释放,GC 管不了!
  3. GC 回收是异步、不确定时间的
     
    你无法控制它什么时候执行,所以非托管资源不能等 GC。
 

 

三、IDisposable 接口:手动释放非托管资源

 

1. 为什么需要它?

 
GC 不管非托管资源,如果不手动释放:
 
  • 文件被占用无法删除
  • 数据库连接耗尽
  • 内存泄漏
  • 程序卡顿、崩溃
 
解决方案:让类实现 IDisposable
 

2. IDisposable 只有一个方法

 
public interface IDisposable
{
    void Dispose();
}
 

3. 实现模板

 
public class MyResource : IDisposable
{
    // 标记是否已经释放
    private bool _disposed = false;

    // 公共方法:外部调用释放
    public void Dispose()
    {
        Dispose(true);
        // 告诉 GC:不用调用析构函数了,我已经手动释放完了
        GC.SuppressFinalize(this);
    }

    // 核心释放逻辑
    protected virtual void Dispose(bool disposing)
    {
        if (_disposed) return;

        if (disposing)
        {
            // 释放 托管资源
        }

        // 释放 非托管资源(文件、数据库连接、流等)
        // 例:file.Close();  dbConnection.Close();

        _disposed = true;
    }

    // 析构函数:防止用户忘记调用 Dispose,GC 时兜底
    ~MyResource()
    {
        Dispose(false);
    }
}
 
 

 

四、using 模式:优雅的自动释放

 
using 是 C# 给 IDisposable 准备的语法糖,自动调用 Dispose (),无论代码是否正常执行、是否报错。
 

1. 核心作用

 
  • 包裹实现了 IDisposable 的对象
  • 代码块结束 → 自动释放资源
  • 异常也能保证释放,绝对安全
 

2. 两种写法

 

写法 1:推荐(简洁)

 
using (var stream = new FileStream("test.txt", FileMode.Create))
{
    // 使用流
    stream.WriteByte(1);
} 
// 离开大括号:自动调用 stream.Dispose()
 

写法 2:多个资源(C# 8.0+)

 
using var stream = new FileStream("test.txt", FileMode.Create);
using var reader = new StreamReader(stream);
// 方法结束时自动释放
 

3. 本质原理

 
using 等价于 try-finally
 
FileStream stream = null;
try
{
    stream = new FileStream("test.txt", FileMode.Create);
}
finally
{
    stream?.Dispose();
}
 
 

 

五、四者关系总结

 
  1. 内存基础:栈自动释放,堆靠 GC
  2. GC:只回收托管堆,不管非托管资源
  3. IDisposable:定义手动释放非托管资源的规范
  4. using:简化 IDisposable,自动调用 Dispose(),安全不遗漏
 

 

六、开发实践

 
  1. 只要用到文件、数据库、流、Socket、图片,一律用 using
  2. 自己写的类包含非托管资源 → 实现 IDisposable
  3. 不要手动调用 GC.Collect ()
  4. using 优先于手动 Dispose ()
 

 

总结

 
  1. 栈自动释放,堆由 GC 管理,GC 不处理非托管资源
  2. IDisposable 是手动释放非托管资源的标准接口
  3. using 是自动调用 Dispose 的语法糖,安全、简洁、你就用吧
  4. 文件 / 流 / 数据库连接 → 全部套 using


来源:https://www.cnblogs.com/chuansheng/p/19909164
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

相关侵权、举报、投诉及建议等,请发 E-mail:qiongdian@foxmail.com

Powered by Discuz! X5.0 © 2001-2026 Discuz! Team.

在本版发帖返回顶部