You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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. 是否能发现代码中的明显错误?
  2. 或许并无错误,是我的判断有误?

分析与建议

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 15:23:15