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

.Net使用AseClient访问Sybase时大量对象终结引发GC停顿如何优化?

问题场景
  • 高流量 .NET Framework API 服务使用 16.0.2.6 版本 AseClient 驱动(Sybase.AdoNet4.AseClient.dll)连接 Sybase 数据库执行存储过程。
  • 生产环境用 PerfView 做性能分析时,观测到服务存在极高的终结对象计数(Finalized Object Counts),查看入口为:PerfView 转储文件 -> 内存分组(Memory Group) -> GCStats 菜单项,统计结果如下:
    Finalized Object Counts
  • 该问题导致 .NET GC 压力陡增,所有连接 Sybase 的服务均出现频繁 GEN1、GEN2 垃圾回收。
  • 性能最差的服务数据库访问代码如下:
using (var connection = new AseConnection(ConnectionString)) {
using (var command = new AseCommand("exampleproc", connection) { CommandType = CommandType.StoredProcedure, Connection = connection })
{
    connection.Open();

    using (var dr = command.ExecuteReader())
    {
        var nextResult = true;

        while (nextResult)
        {
            while (dr.Read())
            {
                //业务逻辑
            }

            nextResult = dr.NextResult();
        }
    }
}}

核心目标:通过可落地的手段避免资源延迟终结,降低GC压力,减少GC停顿。

优化方案

1. 修复驱动本身的资源泄漏缺陷

16.0.2.6 版本的官方 AseClient 存在明确的非托管资源释放Bug:多结果集遍历场景下,AseDataReader、AseCommand 持有的内部非托管句柄,即使包裹在 using 块中也不会被显式释放,最终全部进入终结器队列等待GC处理,这就是终结对象计数异常升高的根因。

  • 优先升级驱动到 16.0 SP03 及以上稳定版本,SAP已在后续补丁中修复该类资源释放问题。
  • 若受业务限制暂时无法升级驱动,必须在代码中显式调用相关对象的资源释放方法,不能完全依赖 using 块的默认释放逻辑。

2. 修正数据访问层的资源释放逻辑

现有代码虽然使用了 using 语法,但没有覆盖多结果集场景下的临时资源释放,调整为以下写法:

using (var connection = new AseConnection(ConnectionString))
{
    connection.Open();
    using (var command = new AseCommand("exampleproc", connection) 
    { 
        CommandType = CommandType.StoredProcedure 
    })
    {
        // 传入CommandBehavior.CloseConnection,确保Reader释放时同步关闭连接持有的非托管句柄
        using (var dr = command.ExecuteReader(CommandBehavior.CloseConnection))
        {
            do 
            {
                while (dr.Read())
                {
                    // 处理当前结果集数据
                }
                // 主动释放当前结果集占用的非托管内存,避免残留到终结器队列
                dr.Cancel();
            } while (dr.NextResult());
        }
    }
}

注意不要在 AseCommand 初始化时重复赋值 Connection 属性,构造函数传入后重复赋值会触发驱动内部多余的对象初始化,生成额外的待终结对象。

3. 合理配置连接池减少重复资源创建

高流量场景下每次新建数据库连接都会生成大量非托管资源对象,开启连接池复用连接、命令的核心句柄,可大幅降低待终结对象总量:

  • 在连接字符串中增加配置 Pooling=true;Min Pool Size=10;Max Pool Size=100;Connection Lifetime=300,最小/最大池大小根据服务实际QPS调整,避免连接频繁创建、销毁。
  • 无明确分布式事务需求时,不要在连接字符串中开启DTC相关配置,DTC包装对象是高终结对象计数的另一常见来源。

4. 短期兜底优化降低GC停顿影响

如果短期内无法升级驱动、也无法全量修改代码,可通过以下配置降低影响:

  • 服务启动时配置 System.Runtime.GCSettings.LatencyMode = GCLatencyMode.SustainedLowLatency,减少GEN2回收的阻塞时长。
  • 对高频调用的存储过程,通过 AseCommand.Prepare() 预编译命令对象并复用实例,避免每次请求都新建命令对象生成临时垃圾。

内容的提问来源于stack exchange,提问作者Michel van Engelen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 18:33:26