根本原因是oracle.manageddataaccess驱动在高并发调用存储过程时未及时释放游标、连接或会话上下文,叠加.net 7的gc行为变化导致内存堆积;具体表现为ref cursor未关闭、oraclecommand未dispose、pl/sql侧dbms_sql/lob资源未清理,以及连接池参数配置不当放大泄漏效应。

根本原因不是.NET 7本身,而是 Oracle 客户端驱动(Oracle.ManagedDataAccess)在高并发调用存储过程时未及时释放游标、连接或会话上下文,叠加 .NET 7 的 GC 行为变化导致内存堆积。
Oracle.ManagedDataAccess 游标未关闭引发内存泄漏
使用 OracleCommand 调用存储过程时,若未显式调用 Dispose() 或未用 using 包裹,底层 Oracle 游标(尤其是 REF CURSOR 输出参数)会长期驻留于会话 PGA 中,不随 .NET 对象回收自动释放。Oracle 12c+ 默认启用游标共享,但未关闭的游标仍会持续占用 PGA 内存,且不会被 GC.Collect() 清理。
- 常见错误写法:
var cmd = new OracleCommand("pkg.proc", conn); cmd.ExecuteNonQuery();—— 忘记cmd.Dispose() - REF CURSOR 返回后未调用
OracleRefCursor.Close()或未读取完结果集,会导致游标句柄悬空 - 在异步场景下混用
ExecuteReaderAsync()+ 手动Dispose(),易因执行流跳转遗漏释放
.NET 7 的 GC 策略对 Oracle 连接池更敏感
.NET 7 默认启用 Server GC 并优化了大对象堆(LOH)压缩,但 Oracle.ManagedDataAccess 的连接池(OracleConnectionPool)内部缓存大量元数据和绑定信息,这些对象常落在 LOH 上。当连接复用频繁、存储过程参数结构复杂(如嵌套表、自定义类型),LOH 分配碎片化加剧,GC 压力上升,表现为内存占用缓慢但持续增长。
- 表现特征:内存曲线呈斜线上升,
dotMemory显示大量OracleInternal.ServiceObjects.OracleConnectionImpl和OracleInternal.ServiceObjects.OracleParameterCollection实例堆积 - 关键参数影响:
Connection Timeout和Max Pool Size设置过大(如 > 100),会放大池内未清理状态的累积效应 - 对比 .NET 6:.NET 7 的 GC 更激进地延迟 LOH 回收,若 Oracle 驱动未及时清空参数缓存,问题更明显
存储过程中 PL/SQL 侧隐性资源未释放
.NET 层看似调用结束,但 Oracle 存储过程内部若使用了 DBMS_SQL、动态 SQL、临时 LOB(DBMS_LOB.CREATETEMPORARY)或未关闭的 UTL_FILE 句柄,这些资源绑定在会话 PGA 中,直到连接归还池或会话终止才释放。而 Oracle.ManagedDataAccess 默认连接池超时长(Connection Lifetime=0),导致 PGA 占用长期滞留。
- 典型诱因:
DBMS_SQL.OPEN_CURSOR后未配对CLOSE_CURSOR;DBMS_LOB.CREATETEMPORARY后未调用FREE_TEMPORARY - 诊断方法:在问题时段查
v$session的pga_used_mem,再关联v$open_cursor确认游标数是否异常偏高 - 规避手段:存储过程中强制加
EXCEPTION块兜底清理,或改用静态 SQL 替代动态构造
真正卡点在于“跨层资源归属模糊”——.NET 认为 OracleCommand 释放即结束,Oracle 认为会话未断就保留上下文。不显式干预,这两层的内存生命周期就永远错位。











