【C#】性能优化

一、代码层面的优化技巧

1. 减少内存分配

问题:频繁创建对象导致GC疯狂回收!

效果:优化后GC压力降低90%,内存占用像坐过山车一样稳定!

C# 使用 .NET 的自动内存管理机制,由 垃圾回收器(GC) 负责回收不再使用的对象所占用的内存。

.NET GC 将托管堆分为三代:

回收频率:Gen 0 > Gen 1 > Gen 2

Gen 2 回收(Full GC)代价最高,应尽量避免。

  • Gen 0:新分配的对象,回收最频繁。
  • Gen 1:从 Gen 0 存活下来的对象。
  • Gen 2:长期存活的对象(如静态变量、缓存等)。
cs 复制代码
// 传统方式(频繁分配)
public List<int> GetNumbers(int count) {
    List<int> numbers = new List<int>();
    for (int i = 0; i < count; i++) {
        numbers.Add(i); // 每次Add都可能分配新内存
    }
    return numbers;
}

// 优化方式(对象池复用)
public class NumberPool {
    private static readonly ArrayPool<int> _pool = ArrayPool<int>.Shared;
    public int[] GetNumbers(int count) {
        int[] buffer = _pool.Rent(count); // 从池中租用数组
        for (int i = 0; i < count; i++) {
            buffer[i] = i;
        }
        return buffer;
    }
    public void ReturnBuffer(int[] buffer) {
        _pool.Return(buffer); // 归还数组到池
    }
}

2. 减少装箱拆箱

效果:泛型集合在处理值类型时不需要装箱操作,性能提升30%+!

  • 装箱会将值类型转为引用类型,触发堆分配。
  • 使用泛型(如 List<int> 而非 ArrayList)可避免装箱。
cs 复制代码
// 问题:装箱拆箱
int num = 10;
object obj = num; // 装箱
int newNum = (int)obj; // 拆箱

// 优化:使用泛型集合
List<int> intList = new List<int> { 1, 2, 3, 4, 5 };
// 代替 ArrayList
ArrayList arrayList = new ArrayList { 1, 2, 3, 4, 5 };

3. 算法优化:从O(n²)到O(n)

效果:1000个订单的处理时间从10秒降到0.1秒!

cs 复制代码
// 问题代码:O(n²)的嵌套循环
public List<Order> FilterOrdersByCustomer(List<Order> orders, string customerId) {
    List<Order> result = new List<Order>();
    foreach (var order in orders) {
        foreach (var customer in orders) {
            if (customer.CustomerId == customerId) {
                result.Add(order);
            }
        }
    }
    return result;
}

// 优化代码:O(n)的哈希表
public List<Order> FilterOrdersByCustomerOptimized(List<Order> orders, string customerId) {
    List<Order> result = new List<Order>();
    Dictionary<string, List<Order>> customerOrders = new Dictionary<string, List<Order>>();
    foreach (var order in orders) {
        if (!customerOrders.ContainsKey(order.CustomerId)) {
            customerOrders[order.CustomerId] = new List<Order>();
        }
        customerOrders[order.CustomerId].Add(order);
    }
    if (customerOrders.TryGetValue(customerId, out List<Order> ordersForCustomer)) {
        result = ordersForCustomer;
    }
    return result;
}

4.减少不必要的对象分配

  • 避免在循环中创建对象(尤其是引用类型)。
  • 使用 值类型(struct) 替代小对象(注意装箱问题)。
  • 重用对象(对象池、缓存)。
cs 复制代码
// 不推荐:每次调用都分配新数组
byte[] buffer = new byte[1024];

// 推荐:栈分配或复用缓冲区
Span<byte> buffer = stackalloc byte[1024]; // 仅限 unsafe 或 .NET Core+

二、系统架构层面的优化

1. 多线程优化

cs 复制代码
int sum = 0;
object lockObj = new object();
Parallel.ForEach(numbers, (number) => {
    lock (lockObj) {
        sum += number;
    }
});

注意:合理利用多线程可以充分发挥多核处理器的性能,但要注意线程同步问题,避免死锁和竞态条件。

2. 缓存策略:减少数据库访问

cs 复制代码
private static MemoryCache _cache = new MemoryCache(new MemoryCacheOptions());
public User GetUser(int userId)
{
    User user = _cache.Get<User>($"user_{userId}");
    if (user == null)
    {
        user = GetUserFromDatabase(userId); // 从数据库获取用户信息
        _cache.Set($"user_{userId}", user, TimeSpan.FromMinutes(30));
    }
    return user;
}

效果:减少80%的数据库查询,响应速度提升5倍!

三、高级优化技术

1. 使用Span避免堆内存分配

效果:零堆内存分配,GC压力降低90%!

cs 复制代码
public class TensorProcessor {
    // 使用Span<T>避免分配临时数组
    public void Normalize(float[] data, int size) {
        Span<float> span = data; // 直接映射到原始数组
        for (int i = 0; i < size; i++) {
            span[i] = (span[i] - 128f) / 255f; // 归一化操作
        }
    }
    
    // 传统方式(频繁GC)
    public void NormalizeLegacy(float[] data, int size) {
        float[] normalized = new float[size]; // 分配新数组
        for (int i = 0; i < size; i++) {
            normalized[i] = (data[i] - 128f) / 255f;
        }
    }
}

2. 内存池MemoryPool的实战应用

cs 复制代码
public class MemoryPoolExample {
    private readonly MemoryPool<float> _pool = MemoryPool<float>.Shared;
    
    public void ProcessBatch(int batchSize, int size) {
        for (int i = 0; i < batchSize; i++) {
            var memory = _pool.Rent(size); // 从池中租借内存
            try {
                var span = memory.Span; // 填充数据并处理...
            } finally {
                _pool.Return(memory); // 归还内存
            }
        }
    }
}

四、性能调优的方法论

性能调优不是"单点突破",而是"全链路优化",可分为四个层面:

  1. 代码层面:算法、数据结构、内存管理
  2. 框架层面:选择合适的框架和库
  3. 系统层面:操作系统、网络、数据库
  4. 硬件层面:CPU、内存、磁盘、网络

五、其他

1.用工具"诊断"性能瓶颈

就像医生用听诊器听心跳,我们也要用工具来"听"代码的心跳声:

  • Visual Studio自带的诊断工具:可以分析CPU、内存和UI流畅性
  • BenchmarkDotNet:专业性能基准测试工具
  • dotnet-trace:收集运行中的性能数据
  • 大多数现代服务应用应使用 Server GC
  • 不要手动调用 GC.Collect(),除非有明确性能数据支持。
  • 使用 PerfViewdotMemory 分析 GC 停顿时间和分配热点。
  • 结合 对象池(ArrayPool, MemoryPool)Span/Memory 减少分配。

2.优化建议

| 优化方向 | 建议 |
| 内存分配 | 减少 new,复用对象,使用池化 |
| GC 压力 | 避免 LOH,减少 Gen 2 对象 |
| 资源管理 | 正确使用 IDisposable |
| 类型选择 | 合理使用 struct,避免装箱 |

工具辅助 使用 PerfView 分析 GC 行为
应用类型 推荐 GC 模式 理由
桌面 GUI 应用(WPF/WinForms) Workstation GC(默认) 用户交互敏感,需低延迟,避免 UI 卡顿
ASP.NET Core Web API / 微服务 Server GC 高并发、多请求,需要高吞吐和快速回收
游戏(Unity / .NET) Workstation + 手动优化 需要帧率稳定,避免 GC 停顿;常配合对象池
后台批处理 / 数据处理服务 Server GC 利用多核并行回收,提升处理效率
低内存设备(IoT、嵌入式) Workstation GC 内存受限,Server GC 内存开销大

六、内存的使用和释放

【C#】内存的使用和释放

相关推荐
暮疯不疯1 天前
C#常见术语表格
开发语言·c#
JQLvopkk1 天前
VS2015使用C#连接KepserverEX并操作读写节点
开发语言·c#
流水线上的指令侠1 天前
补充说明——针对《C#:从 0 到 1 创建基于 NUnit + FlaUI 的 WPF UI 自动化测试项目》
功能测试·ui·c#·自动化·wpf
流水线上的指令侠1 天前
C# 实战:从 0 到 1 搭建基于 NUnit + FlaUI 的 WPF UI 自动化测试项目
功能测试·ui·c#·自动化·wpf·visual studio
gc_22991 天前
学习C#调用OpenXml操作word文档的基本用法(20:学习嵌入文件类)
c#·word·openxml·嵌入文档
玩泥巴的1 天前
如何实现一套.net系统集成多个飞书应用
c#·.net·二次开发·飞书
ghie90901 天前
基于C#实现俄罗斯方块游戏
开发语言·游戏·c#
ccut 第一混1 天前
C# 基于 RS485 与设备通讯(以照度计为例子)
c#·rs485
贾修行1 天前
.NET 全栈开发学习路线:从入门到分布式
c#·.net·wpf·asp.net core·web api·winforms·services