C# API内存泄漏排查求助:请求后内存未释放
C# API 请求后内存持续增长,无法定位泄漏点
我的C# API处理简单请求时,会堆积byte[]、字符串等不必要的数据,每次请求后内存占用增加数KB且从未释放。请求50次后,内存占用增长数MB,即使手动触发GC也无法释放这些内存,已确认存在内存泄漏但找不到泄漏点。
功能说明
该接口通过BearerToken识别用户,返回该用户的所有笔记本数据。
涉及代码
GetCurrentUser(静态方法)
public static async Task<UserModel?> GetCurrentUser(DataBaseContext _db, string bearerToken) { //get from the bearer token the username //create jwt token var token = new JwtSecurityToken(jwtEncodedString: bearerToken); //get the expiry from the token var userName = token.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier)?.Value; //get the whole user form the db var user = _db.User .Include(x => x.Invoices) .Include(x => x.Notebook) .Include(x => x.Logins) .FirstOrDefaultAsync(x => x.Username == userName); return await user; }
GetCurrentUser(私有方法)& GetAll 接口
private async Task<UserModel?> GetCurrentUser() { // Get the bearer token from the header var bearerToken = HttpContext.Request.Headers["bearerToken"].ToString(); var user = await UserCore.GetCurrentUser(_db, bearerToken); return user; } [HttpGet] [Route("GetAll")] public async Task<IActionResult> GetAll() { //set the users traceId Guid traceId = Guid.NewGuid(); //get the user var user = await GetCurrentUser(); if (user == null) return new BadRequestObjectResult(ResponseMgr.CreateResponse(400, traceId, "The user does not exist")); //get all notes var notes = user.Notebook.ToList(); //return all notes return new OkObjectResult(ResponseMgr.CreateResponse(200, traceId, "All notes", new Dictionary<string, object> { { "notes", notes } })); }
堆内存分析
堆图显示存在重复的字符串列表,其中包含某笔记本的表情符号内容,请求结束后这些数据未被释放。堆图列依次为「value」「wasted」「count」,底部为「total」。
排查建议
- 检查DbContext生命周期:确认
DataBaseContext是否注册为Scoped生命周期。如果是Singleton,EF会缓存所有加载的实体,导致内存无法释放,这是此类问题的常见原因。 - 移除不必要的Include:当前
GetCurrentUser中加载了Invoices、Logins,但接口仅需Notebook数据,多余的关联数据会被上下文持有,增加内存占用。只保留Include(x => x.Notebook)即可。 - 排查ResponseMgr.CreateResponse实现:如果该方法使用静态集合缓存响应数据,或持有对象的强引用,会导致数据无法被GC回收。检查是否存在静态变量、单例缓存等逻辑。
- 确认DbContext释放逻辑:ASP.NET Core中Scoped DbContext会在请求结束自动释放,但如果有额外代码持有DbContext的引用(比如静态变量、长期存活的对象),会导致关联的实体无法被回收。
- 检查JwtSecurityToken相关缓存:创建
JwtSecurityToken时,是否有第三方库或自定义代码缓存了token或其Claims?静态缓存会导致字符串(如用户名)被长期持有。 - 用内存分析工具追踪引用链:使用Visual Studio内存探查器或dotMemory,找到未释放字符串的根引用,查看是被DbContext、静态变量还是其他长期对象持有,精准定位泄漏点。
内容的提问来源于stack exchange,提问作者Schecher_1
相关产品推荐
相关产品推荐

