性能瓶颈会降低应用程序的效率、可扩展性和用户体验。许多 .NET 开发者在不知不觉中陷入了一些反模式,随着时间的推移,这些反模式会降低应用程序的性能。在本文中,我们将探讨十大 .NET 性能反模式,解释它们为何会产生问题,并展示如何使用优化后的解决方案来修复它们。
创建过多的短期对象会导致频繁的垃圾回收(GC)周期,从而影响应用程序的性能。
Span
和 Memory
减少分配。GCSettings.LargeObjectHeapCompactionMode
进行垃圾回收调优。// 不要这样做:
var data = new byte[1024];
// 使用 MemoryPool 来重用已分配的内存:
var pool = MemoryPool<byte>.Shared;
using (var owner = pool.Rent(1024))
{
var memory = owner.Memory;
// 在这里处理内存
}
在异步方法上调用 .Result
或 .GetAwaiter().GetResult()
会阻塞线程,并且可能导致死锁。
async/await
。// 反模式
public string GetData()
{
return GetDataAsync().Result; // 阻塞线程
}
// 修复
public async Task<string> GetDataAsync()
{
return await FetchDataFromServiceAsync();
}
// 反模式
var orders = context.Orders.ToList();
foreach (var order in orders)
{
var customer = context.Customers.Find(order.CustomerId); // N + 1 问题
}
// 修复
var ordersWithCustomers = context.Orders.Include(o => o.Customer).ToList();
反射由于元数据检查会产生显著的性能开销。
// 反模式
var type = typeof(MyClass);
var property = type.GetProperty("MyProperty");
var value = property.GetValue(instance);
// 修复
var propertyDelegate = (Func<MyClass, object>)Delegate.CreateDelegate(
typeof(Func<MyClass, object>), null, type.GetProperty("MyProperty").GetMethod);
var valueOptimized = propertyDelegate(instance);
字符串是不可变的,重复拼接会在内存中创建多个新的字符串对象。
对于重复的字符串操作,使用 StringBuilder
。
// 反模式
string result = "";
for (int i = 0; i < 1000; i++)
{
result += i.ToString();
}
// 修复
var sb = new StringBuilder();
for (int i = 0; i < 1000; i++)
{
sb.Append(i);
}
string optimizedResult = sb.ToString();
多次执行相同的昂贵计算会浪费资源。
MemoryCache
、Redis 或 Lazy
进行缓存。private static readonly MemoryCache _cache = new MemoryCache(new MemoryCacheOptions());
public string GetExpensiveData(string key)
{
if (!_cache.TryGetValue(key, out string cachedData))
{
cachedData = ComputeExpensiveData();
_cache.Set(key, cachedData, TimeSpan.FromMinutes(10));
}
return cachedData;
}
同步数据库查询会阻塞线程,降低可扩展性。
使用 EF Core 方法的异步版本,如 ToListAsync()
。
// 反模式
var users = context.Users.ToList(); // 阻塞线程
// 修复
var users = await context.Users.ToListAsync();
在热点路径中记录过多日志会减慢执行速度。
// 反模式
_logger.LogInformation("Processing item: {Id}", item.Id);
// 修复
if (_logger.IsEnabled(LogLevel.Debug))
{
_logger.LogDebug("Processing item: {Id}", item.Id);
}
某些 LINQ 操作,如在过滤之前调用 ToList()
,会导致不必要的内存使用。
通过避免过早调用 .ToList()
来使用延迟执行。
// 反模式
var filteredUsers = context.Users.ToList().Where(u => u.IsActive);
// 修复
var filteredUsers = context.Users.Where(u => u.IsActive).ToList();
一次性将大型数据集加载到内存中会导致高内存消耗。
使用 IAsyncEnumerable
来流式传输数据。
public async IAsyncEnumerable<User> GetUsersAsync()
{
await foreach (var user in context.Users.AsAsyncEnumerable())
{
yield return user;
}
}
修复这些反模式将显著提升你的 .NET 应用程序的性能,带来更好的可扩展性和效率。
首先使用 dotnet-trace
或 BenchmarkDotNet
对你的代码进行性能分析,以检测性能瓶颈,然后逐步应用这些修复方法。