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

为何我的Web API每次请求内存占用都会增加近100MB?

问题:每次请求后Web API内存占用增加近100MB

我开发了一个仅通过GET方法返回数据的Web API,但每次发起请求时,RAM占用都会增加近100MB。如果部署在服务器上接收大量请求,这会引发严重问题。我已经尝试过缓存数据,但没有解决问题。

Program.cs 代码

using Microsoft.Extensions.Hosting.WindowsServices;

var builder = WebApplication.CreateBuilder(args);
var options = new WebApplicationOptions
{
    Args = args,
    ContentRootPath = WindowsServiceHelpers.IsWindowsService() ? AppContext.BaseDirectory : default
};

builder.Host.UseWindowsService();
builder.Services.AddControllers();

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

app.Urls.Add("http://*:5436");

app.UseAuthorization();

app.MapControllers();

app.Run();

控制器代码

using Npgsql;

namespace F360Api.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class PedidoController : ControllerBase
    {
        private readonly IConfiguration _config;

        public PedidoController(IConfiguration configuration)
        {
            _config = configuration;
        }

        private List<Dictionary<string, object>> ExecuteQuery(string query, NpgsqlParameter[] parameters = null)
        {
            List<Dictionary<string, object>> result = new List<Dictionary<string, object>>();

            string sqlDataSource = _config.GetConnectionString("F360Api");

            using (NpgsqlConnection connection = new NpgsqlConnection(sqlDataSource))
            {
                connection.Open();
                using (NpgsqlCommand cmd = new NpgsqlCommand(query, connection))
                {
                    if (parameters != null)
                    {
                        cmd.Parameters.AddRange(parameters);
                    }

                    using (NpgsqlDataReader reader = cmd.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            var row = new Dictionary<string, object>();
                            for (int i = 0; i < reader.FieldCount; i++)
                            {
                                row[reader.GetName(i)] = reader[i];
                            }
                            result.Add(row);
                        }
                    }
                }
            }

            return result;
        }

        [HttpGet]
        public IActionResult Get()
        {
            try
            {
                string query = @""; --query here

                List<Dictionary<string, object>> result = ExecuteQuery(query);

                return Ok(result);
            }
            catch (Exception ex)
            {
                return StatusCode(500, $"An error occurred: {ex.Message}");
            }
        }

        [HttpGet("GetByDate")]
        public IActionResult GetByDate([FromQuery] DateTime startDate, [FromQuery] DateTime endDate)
        {
            try
            {
                string query = @""; --query here

                NpgsqlParameter[] parameters =
                {
                    new NpgsqlParameter("@startDate", NpgsqlTypes.NpgsqlDbType.Timestamp) { Value = startDate },
                    new NpgsqlParameter("@endDate", NpgsqlTypes.NpgsqlDbType.Timestamp) { Value = endDate }
                };

                List<Dictionary<string, object>> result = ExecuteQuery(query, parameters);

                return Ok(result);
            }
            catch (Exception ex)
            {
                return StatusCode(500, $"An error occurred: {ex.Message}");
            }
        }
    }
}

问题原因及解决方法

1. 一次性加载大量数据到内存

你的ExecuteQuery方法会把查询结果全部加载到List<Dictionary<string, object>>中,如果查询返回的数据集很大(比如几万甚至几十万条记录),每次请求都会在内存中创建大量对象,直接导致内存飙升。

解决方法:

  • 分页查询:在PostgreSQL的SQL语句中加入LIMIT和OFFSET,让客户端分批次获取数据,避免一次性加载所有数据。
  • 流式返回:改用IAsyncEnumerable<T>结合yield return逐步返回数据,减少内存占用。示例代码:
[HttpGet]
public async IAsyncEnumerable<Dictionary<string, object>> Get()
{
    string query = @"-- 你的查询语句";
    string sqlDataSource = _config.GetConnectionString("F360Api");

    using (NpgsqlConnection connection = new NpgsqlConnection(sqlDataSource))
    {
        await connection.OpenAsync();
        using (NpgsqlCommand cmd = new NpgsqlCommand(query, connection))
        {
            using (NpgsqlDataReader reader = await cmd.ExecuteReaderAsync())
            {
                while (await reader.ReadAsync())
                {
                    var row = new Dictionary<string, object>();
                    for (int i = 0; i < reader.FieldCount; i++)
                    {
                        row[reader.GetName(i)] = reader[i];
                    }
                    yield return row;
                }
            }
        }
    }
}

2. 弱类型字典的额外开销

Dictionary<string, object>属于弱类型存储,值类型数据会被装箱,不仅增加内存占用,还会加重GC回收压力。

解决方法:

  • 创建强类型实体类(比如Pedido),映射数据库表字段,用List<Pedido>代替List<Dictionary<string, object>>。示例:
public class Pedido
{
    public int Id { get; set; }
    public DateTime OrderDate { get; set; }
    // 其他对应数据库列的字段
}

修改ExecuteQuery方法返回强类型集合:

private List<Pedido> ExecuteQuery(string query, NpgsqlParameter[] parameters = null)
{
    List<Pedido> result = new List<Pedido>();
    string sqlDataSource = _config.GetConnectionString("F360Api");

    using (NpgsqlConnection connection = new NpgsqlConnection(sqlDataSource))
    {
        connection.Open();
        using (NpgsqlCommand cmd = new NpgsqlCommand(query, connection))
        {
            if (parameters != null)
                cmd.Parameters.AddRange(parameters);

            using (NpgsqlDataReader reader = cmd.ExecuteReader())
            {
                while (reader.Read())
                {
                    result.Add(new Pedido
                    {
                        Id = reader.GetInt32(reader.GetOrdinal("id")),
                        OrderDate = reader.GetDateTime(reader.GetOrdinal("order_date")),
                        // 其他字段映射
                    });
                }
            }
        }
    }
    return result;
}

3. 内存泄漏排查

如果以上优化后内存仍持续上涨,需要进一步排查:

  • 使用Visual Studio内存诊断工具或dotMemory分析内存快照,定位未被GC回收的对象。
  • 检查是否存在静态集合持有对象引用,导致无法释放。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 14:20:35