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

.NET Core 8固定对象堆内存泄漏排查求助

固定对象导致的.NET Core内存泄漏排查问题

问题描述与已尝试调试

  • 大型企业级.NET Core 8应用存在内存泄漏:大量固定对象阻碍GC堆压缩,导致堆内留存大量空闲内存
  • Azure环境下内存超12GB时抓取内存转储,发现:
    • 应用实际仅使用约200MB内存
    • 第2代GC堆占9.5GB,其中99%为空闲内存;约3000个固定对象散布堆中,完全阻止堆压缩
    • 泄漏的固定对象为byte[8192],所有字节均为0,无法通过保留树追踪引用来源
  • 已完成验证:项目未启用unsafe代码,代码中无fixed关键字,通过CA2000规则确认所有可释放对象均已正确释放,但仍存在大量无法回收的固定对象

请求

  1. 确认对固定对象相关原理的理解是否正确
  2. 如何定位这些byte[8192]数组的创建来源

项目详情

  • 运行环境:.NET Core 8.0
  • 问题触发条件:仅高负载时出现,无法提供内存转储或完整源码
  • 使用的NuGet包:
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.22.0" />
<PackageReference Include="Microsoft.AspNetCore.SpaProxy" Version="8.0.11" />
<PackageReference Include="Selenium.WebDriver" Version="4.26.1" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="8.0.1" />
<PackageReference Include="Azure.Identity" Version="1.13.1" />
<PackageReference Include="AsyncAwaitBestPractices" Version="8.0.0" />
<PackageReference Include="Azure.ResourceManager" Version="1.13.0" />
<PackageReference Include="Azure.ResourceManager.CosmosDB" Version="1.3.2" />
<PackageReference Include="Azure.ResourceManager.ServiceBus" Version="1.0.1" />
<PackageReference Include="DistributedLock" Version="2.5.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.1" />
<PackageReference Include="MongoDB.Driver" Version="2.9.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Azure.Messaging.ServiceBus" Version="7.18.2" />
<PackageReference Include="Azure.Storage.Blobs" Version="12.22.2" />
<PackageReference Include="Microsoft.ApplicationInsights" Version="2.22.0" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.22.0" />
<PackageReference Include="Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel" Version="2.22.0" />
<PackageReference Include="NeoSmart.AsyncLock" Version="3.2.1" />
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.45.0" />
<PackageReference Include="Braintree" Version="5.28.0" />
<PackageReference Include="System.Security.Cryptography.Xml" Version="8.0.2" />
  • 部署实例差异分析:
    • 实例1(存在泄漏):处理API请求 → 读取Cosmos → 反射处理 → 写入Cosmos → 发布Service Bus消息
    • 实例2(无泄漏):处理Service Bus消息 → 读取Cosmos → 写入Cosmos
    • 推测泄漏根源在API请求处理栈
  • 项目中存在少量Reflection.Emit代码(仅执行8次并存储结果):
var typeSignature = this.GetType().Name + "Suffix";
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(Guid.NewGuid().ToString()), AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
var typeBuilder = moduleBuilder.DefineType(typeSignature,
TypeAttributes.Public |
TypeAttributes.Class |
TypeAttributes.AutoClass |
TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit |
TypeAttributes.AutoLayout,
null);

补充信息

  • 发现29937个Byte[][Pinned Handle]实例,同时存在相近数量的以下类型实例:
    • IOCompletionCallbackHelper
    • FileSystemWatcher+ASyncReadState
    • ThreadPoolBoundHandle
    • PreAllocatedOverlapped
    • ThreadPoolBoundHandlerOverlapped

解答

1. 固定对象原理理解确认

你的基础理解存在局限:

  • 除了unsafe代码中的fixed关键字,.NET运行时或框架/第三方库也可能创建固定对象,常见场景包括:
    • 异步IO操作(如ThreadPoolBoundHandle、PreAllocatedOverlapped关联的缓冲区):这类操作会自动固定字节数组作为IO缓冲区,若关联资源未正确释放,固定句柄会残留
    • P/Invoke互操作时的隐式固定:框架或第三方库的原生调用可能自动固定对象
    • 部分序列化/反序列化库的内部实现逻辑
  • CA2000规则仅覆盖显式实现IDisposable的对象,无法追踪运行时或框架隐式创建的固定资源

2. 定位byte[8192]数组的创建来源

结合你的场景(仅API实例泄漏、关联IO相关类型),按以下步骤定向排查:

针对API栈的重点排查

  • 检查HTTP请求处理中的IO操作:
    • 排查自定义请求/响应拦截器、中间件是否使用字节缓冲区且未正确释放关联资源
    • 验证Microsoft.AspNetCore.SpaProxy的配置与使用:SpaProxy在代理请求时可能使用内部8KB缓冲区,高负载下若资源回收不及时可能导致泄漏
    • 检查文件上传/下载逻辑:这类操作常使用FileStream或自定义缓冲区,若未正确处置可能关联固定对象
  • 排查第三方库的隐式固定:
    • 重点检查Azure.Storage.Blobs:存储操作默认使用8KB缓冲区,若客户端实例未正确重用或处置,可能残留固定句柄
    • 验证Selenium.WebDriver的资源回收:WebDriver的网络请求或文件操作可能使用固定缓冲区,高负载下若未正确关闭会话会导致泄漏

基于运行时诊断的技术手段

  • 启用.NET Event Tracing (ETW):
    • 使用dotnet-trace工具收集包含GC分配事件的追踪日志,筛选byte[8192]的调用栈:
      dotnet-trace collect --providers Microsoft-Windows-DotNETRuntime:0x10000:5,Microsoft-Windows-DotNETRuntimeRundown:0x1000:5 --process-id <PID>
      
    • 用PerfView分析日志,定位分配byte[8192]的代码路径
  • Azure诊断工具:
    • 启用Application Insights Profiler或Snapshot Debugger,捕获高负载时的调用栈,关联固定对象的创建上下文
  • dotnet-dump高级分析:
    • 在内存泄漏触发时抓取转储,使用!dumpheap -type Byte[]筛选固定实例,再通过!gcroot <对象地址>追踪关联的IO资源(如PreAllocatedOverlapped)

Reflection.Emit代码的影响验证

你的Reflection.Emit代码不会导致固定对象泄漏:动态程序集的创建逻辑未涉及unsafe固定或IO操作,仅执行8次且存储结果的场景不会产生大量固定字节数组。

关联补充信息中的IO类型

你发现的ThreadPoolBoundHandle、PreAllocatedOverlapped等类型直接关联异步IO的固定缓冲区,说明泄漏根源是异步IO资源未正确释放:

  • 检查是否存在未完成的异步IO操作,或Overlapped对象未被回收
  • 排查是否有自定义代码使用ThreadPoolBoundHandle但未正确处置

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 15:57:04