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

ASP.NET应用上传文件时内存占用过高且无法回收的问题

ASP.NET文件上传接口内存异常增长排查:UploadFileAsync内存无法释放但UploadStringAsync正常

问题描述

排查ASP.NET应用文件上传内存泄漏问题时,编写了如下测试控制器:

[ApiController]
[Route("[controller]")]
public class MoqController : ControllerBase
{
    [HttpPost()]
    [Route("UploadString")]
    public async Task<IActionResult> UploadStringAsync(string str)
    {
        return Ok(str);
    }

    [HttpPost()]
    [Route("UploadFile")]
    public async Task<IActionResult> UploadFileAsync(IFormFile uploadedFile)
    {
        return Ok();
    }
}

测试发现:

  • 调用UploadFileAsync接口时,应用内存持续上升,垃圾回收器无法释放,每次请求内存增加1-3MB,且增量与上传文件大小关联性弱;
  • 调用UploadStringAsync接口则无此异常。

原因分析

  • IFormFile绑定的底层资源未被触发清理:ASP.NET Core处理IFormFile时,会将请求中的文件内容暂存到内存缓冲区(或磁盘临时文件)。即使你没有显式读取文件内容,框架为了完成IFormFile对象绑定,已经加载了请求体的文件部分到内存中。如果这些缓冲区的资源未被主动触发释放,GC不会立即回收这部分内存。
  • Swagger测试工具的额外内存占用:Swagger在发送文件上传请求后,可能会缓存请求内容或记录请求元数据,频繁测试时这部分内存会持续累积,表现为内存无法释放。
  • 框架内存池的复用机制:ASP.NET Core内部用于处理表单数据的ArrayPool<byte>等内存池,短时间频繁处理文件请求时,会保留部分内存块以备复用,这属于正常机制,但会造成“内存泄漏”的假象。

解决方法

  • 显式释放IFormFile的流资源:即使不需要读取文件内容,也主动打开并释放流,触发底层资源的清理:
    [HttpPost()]
    [Route("UploadFile")]
    public async Task<IActionResult> UploadFileAsync(IFormFile uploadedFile)
    {
        // 显式打开并释放流,确保底层缓冲区被回收
        await using var stream = uploadedFile.OpenReadStream();
        return Ok();
    }
    
  • 调整表单处理配置,减少内存缓存:在Program.cs中配置表单上传的阈值,让超过大小的文件直接写入磁盘,避免内存占用:
    builder.Services.Configure<FormOptions>(options =>
    {
        // 设置单次请求的最大表单长度
        options.MultipartBodyLengthLimit = 10 * 1024 * 1024; // 10MB
        // 超过此阈值的文件部分写入磁盘临时文件,不再占用内存
        options.MemoryBufferThreshold = 40 * 1024; // 40KB
    });
    
  • 测试环境禁用Swagger请求缓存:修改Swagger配置,关闭请求历史记录或缓存,避免测试时的额外内存消耗:
    builder.Services.AddSwaggerGen(options =>
    {
        // 关闭Swagger的请求缓存相关功能(仅测试环境用)
    });
    
  • 验证GC延迟回收(仅排查用):在接口末尾手动触发GC(生产环境禁止使用,会影响性能),确认是否为GC延迟导致的假泄漏:
    [HttpPost()]
    [Route("UploadFile")]
    public async Task<IActionResult> UploadFileAsync(IFormFile uploadedFile)
    {
        await using var stream = uploadedFile.OpenReadStream();
        GC.Collect();
        GC.WaitForPendingFinalizers();
        return Ok();
    }
    

内容的提问来源于stack exchange,提问作者arn9000

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 19:51:01