C# GC回收的方法实现

2025-11-27 19:50

本文主要是介绍C# GC回收的方法实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《C#GC回收的方法实现》本文主要介绍了C#GC回收的方法实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...

介绍 C# 中的 垃圾回收(Garbage Collection, GC) 机制。

一、什么是 GC?

GC(Garbage Collector,垃圾回收器) 是 .NET 运行时(CLR)自动管理内存的核心组件。

它的作用是:

自动找出不再被使用的对象(“垃圾”),并释放它们占用的内存,防止内存泄漏。

你不需要像 C/C++ 那样手动 deletefree,C# 的 GC 会帮你搞定!

二、GC 管理的是哪部分内存?

在 C# 中,内存主要分为两块:

内存区域存放内容是否由 GC 管理
托管堆(Managed Heap)引用类型对象(如 class 实例)✅ 是
栈(Stack)值类型(如 int, struct)、方法局部变量、引用类型的引用(指针)❌ 否(随方法结束自动释放)

注意:string、数组、自定义 class 都分配在托管堆上,由 GC 负责回收。

三、GC 什么时候触发?

GC 不是实时运行的,而是在满足以下条件之一时自动触发

  1. 托管堆内存不足(分配新对象时发现空间不够)
  2. 系统内存压力大(Windows 通知 .NET 内存紧张)
  3. 手动调用 GC.Collect()(不推荐!除非特殊场景)

⚠️ 开发者通常无法精确控制 GC 何时发生,这是设计上的有意为之——让开发者专注业务逻辑。

️ 四、GC 如何判断一个对象是“垃圾”?

核心原则:如果一个对象无法从“根(Root)”访问到,它就是垃圾。

什么是“根(Root)”?

  • 全局/静态变量
  • 方法中的局部变量(包括参数)
  • CPU 寄存器中的引用
  • 本地方法(Native code)持有的引用

示例:

void MyMethod()
{
    var person = new Person(); // person 是局部变量 → 根
    person.Name = "Alice";
} // 方法结束,person 超出作用域 → 不再是根

→ 此时 Person 对象不可达,下次 GC 时会被回收。

五、GC 的核心机制:分代回收(Generational GC)

.NET 的 GC 采用 “分代回收” 策略,把对象按“年龄”分成三代:

代(Generation)特点回收频率
Gen 0新创建的对象⏱️ 最频繁(毫秒级)
Gen 1活过一次 Gen 0 回收的对象中等
Gen 2老对象(活过 Gen 1) 最少(可能几秒甚至几分钟一次)

为什么分代?

经验规律:大多数对象“朝生暮死”(比如临时变量)。
所以优先快速回收 Gen 0,避免全堆扫描,提升性能。

回收过程简述:

  1. GC 暂停所有线程(“Stop-The-World”)
  2. 从“根”出发,标记所有可达对象
  3. 清除未标记对象(垃圾)
  4. 压缩内存:把存活对象往编程堆的一端移动,消除碎片
  5. 晋升:Gen 0 存活对象 → Gen 1;Gen 1 存活 → Gen 2

Gen 2 回收也叫 Full GC,开销最大,应尽量避免频繁发生。

六、如何查看对象在哪一代?

可以使用 GC.GetGeneration(object)

var obj = new object();
Console.WriteLine(GC.GetGeneration(obj)); // 输出 0

GC.Collect(); // 强制回收 Gen 0
Console.WriteLine(GC.GetGeneration(obj)); // 如果 obj 还被引用,输出 1

️ 七、特殊对象:需要“清理”的资源

GC 只负责内存回收,但有些对象持有非托管资源(如文件句柄、数据库连接、网络 socket),这些资源不会自动释放

解决方案:实现IDisposable+Dispose模式

public class MyFile : IDisposable
{
    private FileStream _file;

    public MyFile(string path)
    {
        _file = File.OpenRead(path);
    }

    public void Dispose()
    {
        _file?.Dispose(); // 立即释放非托管资源
        GC.SuppressFinalize(this); // 告诉 GC:不用调用析构函数了
    }

    ~MyFile() // 析构函数(Finalizer)— 最后的保险
    {
        Dispose();
    }
}

// 使用方式(推荐 using)
using (var file = new MyFile("data.txt"))
{
    // 使用文件
} // 自动调用 Dispose()

✅ 最佳实践:对持有非托管资源的类,务必实现 IDisposable,并用 using 语句确保及时释放。

八、GC 对性能的影响 &a编程mp; 优化建议

可能的问题:

  • Stop-The-World:GC 期间所有线程暂停,可能导致 UI 卡顿
  • 频繁分配/回收:大量临时对象 → 频繁 Gen 0 GC → CPU 浪费
  • 大对象堆(LOH):≥85,000 字节的对象分配在 LOH,不会压缩,易产生内存碎片

优化建议:

场景建议
频繁创建小对象✔️ 重用对象(对象池 ObjectPool<T>)
大数组/缓冲区✔️ 使用 ArrayPool<T>.Shared
长时间持有对象✔️ 避免无意中延长对象生命周期(如事件订阅未取消)
高性能场景✔️ 减少分配(如用 Span<T>、stackalloc)
监控 GC✔️ 使用 PerfView、dotMemory 或 GC.CollectionCount
// 查看各代 GC 次数
Console.WriteLine($"Gen 0: {GC.CollectionCount(0)}");
Console.WriteLine($"Gen 1: {GC.CollectionCount(1)}");
Console.WriteLine($"Gen 2: {GC.CollectionCount(2)}");

九、GC vs 手动内存管理(C++)

特性C# GCC++ 手动管理
内存安全✅ 不会野指针、重复释放❌ 容易出错
开发效率✅ 高❌ 低
性能可控性❌ 较低(GC 不可预测)✅ 高
内存碎片✅ GC 会压缩堆❌ 需手动管理

C# 的设计理念:用少量性能代价,换取极高的开发安全性和效率

✅ 总结:关键要点

  1. GC 自动回收托管堆上的无用对象,开发者无需手动释放。
  2. 分代回收(Gen 0http://www.chinasem.cn/1/2) 是核心优化策略,javascript基于“多数对象短命”假设。
  3. GC 不处理非托管资源 → 必须用 IDisposable + using。
  4. 避免频繁分配临时对象,尤其在循环或高频方法中。
  5. 不要随意调用 GC.Collect() —— 通常适得其反。
  6. 大对象(≥85KB)进入 LOH,不压缩,需特别注意。

如果你正在开发高性能应用(如游戏、实时系统),理解 GC 行为至关重要;如果是普通业务系统,只需记住:少 new 临时对象,及时 Dispose 资源,就能避开 90% 的问题。

到此这篇关于C# GC回收的方法实现的文章就介绍到这了,更多相关C# GC回收内容请搜索编程tmNiKSChina编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持China编程(www.chinasem.cn)!

这篇关于C# GC回收的方法实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1156290

相关文章

C#借助Spire.XLS for .NET实现在Excel中添加文档属性

《C#借助Spire.XLSfor.NET实现在Excel中添加文档属性》在日常的数据处理和项目管理中,Excel文档扮演着举足轻重的角色,本文将深入探讨如何在C#中借助强大的第三方库Spire.... 目录为什么需要程序化添加Excel文档属性使用Spire.XLS for .NET库实现文档属性管理Sp

检查 Nginx 是否启动的几种方法

《检查Nginx是否启动的几种方法》本文主要介绍了检查Nginx是否启动的几种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学... 目录1. 使用 systemctl 命令(推荐)2. 使用 service 命令3. 检查进程是否存在4

Python+FFmpeg实现视频自动化处理的完整指南

《Python+FFmpeg实现视频自动化处理的完整指南》本文总结了一套在Python中使用subprocess.run调用FFmpeg进行视频自动化处理的解决方案,涵盖了跨平台硬件加速、中间素材处理... 目录一、 跨平台硬件加速:统一接口设计1. 核心映射逻辑2. python 实现代码二、 中间素材处

Java方法重载与重写之同名方法的双面魔法(最新整理)

《Java方法重载与重写之同名方法的双面魔法(最新整理)》文章介绍了Java中的方法重载Overloading和方法重写Overriding的区别联系,方法重载是指在同一个类中,允许存在多个方法名相同... 目录Java方法重载与重写:同名方法的双面魔法方法重载(Overloading):同门师兄弟的不同绝

MySQL字符串转数值的方法全解析

《MySQL字符串转数值的方法全解析》在MySQL开发中,字符串与数值的转换是高频操作,本文从隐式转换原理、显式转换方法、典型场景案例、风险防控四个维度系统梳理,助您精准掌握这一核心技能,需要的朋友可... 目录一、隐式转换:自动但需警惕的&ld编程quo;双刃剑”二、显式转换:三大核心方法详解三、典型场景

Java数组动态扩容的实现示例

《Java数组动态扩容的实现示例》本文主要介绍了Java数组动态扩容的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1 问题2 方法3 结语1 问题实现动态的给数组添加元素效果,实现对数组扩容,原始数组使用静态分配

Python实现快速扫描目标主机的开放端口和服务

《Python实现快速扫描目标主机的开放端口和服务》这篇文章主要为大家详细介绍了如何使用Python编写一个功能强大的端口扫描器脚本,实现快速扫描目标主机的开放端口和服务,感兴趣的小伙伴可以了解下... 目录功能介绍场景应用1. 网络安全审计2. 系统管理维护3. 网络故障排查4. 合规性检查报错处理1.

MySQL快速复制一张表的四种核心方法(包括表结构和数据)

《MySQL快速复制一张表的四种核心方法(包括表结构和数据)》本文详细介绍了四种复制MySQL表(结构+数据)的方法,并对每种方法进行了对比分析,适用于不同场景和数据量的复制需求,特别是针对超大表(1... 目录一、mysql 复制表(结构+数据)的 4 种核心方法(面试结构化回答)方法 1:CREATE

Python轻松实现Word到Markdown的转换

《Python轻松实现Word到Markdown的转换》在文档管理、内容发布等场景中,将Word转换为Markdown格式是常见需求,本文将介绍如何使用FreeSpire.DocforPython实现... 目录一、工具简介二、核心转换实现1. 基础单文件转换2. 批量转换Word文件三、工具特性分析优点局

Springboot3统一返回类设计全过程(从问题到实现)

《Springboot3统一返回类设计全过程(从问题到实现)》文章介绍了如何在SpringBoot3中设计一个统一返回类,以实现前后端接口返回格式的一致性,该类包含状态码、描述信息、业务数据和时间戳,... 目录Spring Boot 3 统一返回类设计:从问题到实现一、核心需求:统一返回类要解决什么问题?