Blazor Server .NET Blob服务疑似内存泄漏问题排查求助
问题描述
我在Blazor应用中新增了分块上传文件至Blob存储的功能,功能运行正常。但通过Visual Studio观察进程内存发现,原本稳定在80MB的应用,每次上传时内存占用会增加10-20MB且不会回落,且每次上传都会持续增长。此前使用100MB块大小上传时,内存会增长100-200MB。我不确定这是内存泄漏还是GC回收延迟。
上传函数代码如下:
public async Task UploadFileToBlobStorage(Stream stream, string fileName, string containerName,string contentType,IProgress<long> progress,CancellationToken cancellationToken) { _logger.LogInformation($"Uploading file {fileName} to container {containerName}"); var blobServiceClient = new BlobServiceClient(new Uri($"https://{accountName}.blob.core.windows.net"), new DefaultAzureCredential()); var containerClient = blobServiceClient.GetBlobContainerClient(containerName); await containerClient.CreateIfNotExistsAsync(cancellationToken:cancellationToken); var blockblobClient = containerClient.GetBlockBlobClient(fileName); var blockIds = new List<string>(); var buffer = new byte[10 * 1024 * 1024]; // 10 MB int bytesRead; int blockNumber = 0; long totalBytesRead = 0; var blobHttpHeader = new BlobHttpHeaders { ContentType = contentType, ContentDisposition = $"attachment; filename={fileName}", }; while ((bytesRead = await stream.ReadAsync(buffer.AsMemory( 0, buffer.Length), cancellationToken)) > 0) { var blockId = Convert.ToBase64String(Encoding.UTF8.GetBytes(blockNumber.ToString("d6"))); blockIds.Add(blockId); using (var blockStream = new MemoryStream(buffer, 0, bytesRead)) { await blockblobClient.StageBlockAsync(blockId, blockStream,cancellationToken: cancellationToken); } totalBytesRead += bytesRead; progress?.Report(totalBytesRead); blockNumber++; } await blockblobClient.CommitBlockListAsync(blockIds,httpHeaders:blobHttpHeader,cancellationToken:cancellationToken); _logger.LogInformation($"File {fileName} uploaded to container {containerName} successfull"); }
传入该函数的stream是通过using声明创建的IBrowserFile:
using var stream = _selectedFile.OpenReadStream(maxAllowedSize: long.MaxValue);
希望得到以下建议:
- 是否能发现代码中的明显错误?
- 或许并无错误,是我的判断有误?
分析与建议
1. 代码中的潜在问题
- 重复创建BlobServiceClient:每次上传都新建
BlobServiceClient实例不合理,该类是线程安全且设计为可复用的,重复创建会额外消耗资源、增加内存负担。建议将BlobServiceClient注册为单例服务(比如在Program.cs中通过AddSingleton<BlobServiceClient>注入),全局复用同一个实例。 - 大对象GC压力:10MB的
buffer属于大对象堆(LOH)中的对象,.NET对LOH的回收频率远低于普通对象堆。每次上传都创建新的大数组,会导致LOH内存占用持续累积,直到GC触发全量回收。可以改用ObjectPool<byte[]>来复用buffer,减少大对象的频繁创建与销毁。 - BlockIds列表内存占用:上传大文件时,
blockIds会存储大量Base64格式的块ID字符串,虽然上传完成后该列表会被标记为可回收,但大列表本身也会占用不少内存。不过这属于临时对象,正常情况下上传完成后会被GC处理,但若连续多次上传,可能叠加GC延迟效应。 - MemoryStream使用无明显问题:这里通过
MemoryStream(buffer, 0, bytesRead)复用了buffer的内存,且用using包裹确保及时释放,这部分代码合理,不会造成额外内存泄漏。
2. 关于内存增长的判断
- 大概率是GC回收延迟:.NET的GC并非实时回收,尤其是大对象堆,默认不会主动压缩,只有当系统内存压力达到阈值时才会触发回收。你看到的内存增长可能是临时未回收的对象占用,而非泄漏。可以尝试在上传完成后手动触发GC(仅用于测试,生产环境请勿使用),若内存回落则可确认是GC延迟问题。
- 区分泄漏与延迟的方法:使用Visual Studio的内存诊断工具,多次上传前后分别抓取内存快照,对比是否有大量未被回收的对象(如
BlobServiceClient实例、buffer数组等)。如果这些对象在上传完成后仍被其他对象持有引用,说明存在内存泄漏;如果没有引用只是未被GC回收,就是延迟问题。
内容的提问来源于stack exchange,提问作者RoXTar
相关产品推荐
相关产品推荐

