分类 C# 下的文章

在 C# 中,内存优化是提升应用性能和稳定性的关键。以下是常见的内存优化方法,结合示例和最佳实践:

一、对象分配与生命周期优化

1. 减少临时对象创建

  • 问题:频繁创建短生命周期对象会导致 GC 压力增大。

  • 优化方法

    • 使用StringBuilder替代字符串拼接(尤其在循环中)。
    • 复用对象实例(如使用对象池)。

    示例(反例)

    string result = "";for (int i = 0; i < 1000; i++) {    result += i.ToString(); // 每次拼接创建新字符串}

    优化后

    StringBuilder sb = new StringBuilder();for (int i = 0; i < 1000; i++) {    sb.Append(i); // 复用StringBuilder实例}string result = sb.ToString();

2. 使用值类型替代引用类型

  • 场景:频繁创建的小型对象(如坐标点、状态标志)。

  • 优化方法

    • 使用struct替代class
    • 使用Span<T>/Memory<T>避免内存分配。

    示例

    public readonly struct Point { // 值类型    public double X { get; }    public double Y { get; }        public Point(double x, double y) => (X, Y) = (x, y);}

3. 避免装箱拆箱

  • 问题:值类型与object之间的转换会产生临时对象。

  • 优化方法

    • 使用泛型集合(如List<T>)替代非泛型(如ArrayList)。
    • 使用System.ValueTuple替代早期的Tuple类。

    示例(反例)

    ArrayList list = new ArrayList();list.Add(42); // 装箱:int → objectint value = (int)list[0]; // 拆箱:object → int

    优化后

    List<int> list = new List<int>();list.Add(42); // 直接存储int,无装箱int value = list[0]; // 无拆箱

二、集合与数组优化

1. 预分配集合容量

  • 问题:动态扩容会导致内存复制和旧数组垃圾。

  • 优化方法

    • 使用带初始容量的构造函数(如new List<int>(100))。

    示例

    // 预分配足够容量,避免多次扩容var users = new List<User>(1000); 

2. 使用高效集合类型

  • 场景:根据访问模式选择合适的集合。

  • 推荐类型

    • Dictionary<TKey, TValue>:快速查找(O (1))。
    • HashSet<T>:唯一元素集合。
    • ConcurrentDictionary<TKey, TValue>:线程安全的字典。

    示例

    // 使用字典替代List,提升查找性能var userMap = new Dictionary<int, User>();userMap.TryGetValue(userId, out var user); // O(1)时间复杂度

3. 避免大型稀疏数组

  • 问题:大量空元素的数组浪费内存。
  • 优化方法
    • 使用Dictionary<int, T>SparseArray(需自定义)。

三、内存管理与垃圾回收

1. 控制对象生命周期

  • 原则:及时释放不再使用的资源。

  • 方法

    • 使用using语句管理实现IDisposable的对象。
    • 避免静态集合持有大量对象引用。

    示例

    using (var stream = new FileStream("data.txt", FileMode.Open)) {    // 使用stream...} // 自动调用Dispose()释放资源

ManualResetEvent被用于在 两个或多个线程间 进行线程信号发送。

多个线程可以通过调用ManualResetEvent对象的WaitOne方法进入等待或阻塞状态。当控制线程调用Set()方法,所有等待线程将恢复并继续执行。

以下是使用ManualResetEvent的例子,确保多线程调用时 First->Second->Third 的顺序不变。若看完仍有疑惑,请点击传送门

using System.Threading;

public class Foo {

private readonly ManualResetEvent firstDone = new ManualResetEvent(false);
private readonly ManualResetEvent secondDone = new ManualResetEvent(false);    

public Foo() {
    
}

public void First(Action printFirst) {
    
    // printFirst() outputs "first". Do not change or remove this line.
    printFirst();
    firstDone.Set();
}

public void Second(Action printSecond) {
    firstDone.WaitOne();
    // printSecond() outputs "second". Do not change or remove this line.
    printSecond();
    secondDone.Set();
}

public void Third(Action printThird) {
    secondDone.WaitOne();
    // printThird() outputs "third". Do not change or remove this line.
    printThird();
}
}