使用DbDataReader读取(迭代)数据时,管理内存分配很大

问题描述 投票:1回答:1

我编写了一个在连接到远程oracle数据库(master)的多台客户机上运行的应用程序,以同步它们的本地开源数据库(slave)。这到目前为止工作正常。但有时需要对本地表进行完全初始化(删除后插入所有主数据库行)。如果主表足够大(ColumnCount或DataType / DataSize和某个RowSize),则应用程序有时会遇到OutOfMemoryException。该应用程序在带有.NET 4.0的Windows计算机上运行。 ODP.NET的版本是4.122.18.3。 Oracle数据库是12c(12.1.0.2.0)。

我不想向任何用户显示数据(应用程序在后台运行),否则我可以做一些分页或过滤。由于并非所有表都包含键或能够自动排序,因此很难获取部分表。本地表的初始化应该在一个事务中完成,而不需要多次部分提交。我可以将问题归结为一个简单的代码示例,显示我没想到的托管内存分配。在这一点上,我不确定如何解释或解决问题。

using (var connection = new OracleConnection(CONNECTION_STRING))
{
    connection.Open();

    using (var command = connection.CreateCommand())
    {
        command.CommandText = STATEMENT;

        using (var reader = command.ExecuteReader())
        {
            while (reader.Read())
            {
                //reader[2].ToString();
                //reader.GetString(2);
                //reader.GetValue(2);
            }
        }
    }
}

当取消注释三个reader。*中的任何一个时,所请求的列数据的内存似乎由ODP.NET(OracleInternal.Network.OraBuf)在内部固定每个记录。要求几千条记录,这似乎不是问题。但是当获取100k +记录时,内存分配会达到数百MB,从而导致OutOfMemoryException。指定列的数据越多,OOM发生得越快(主要是NVARCHAR2)。另外调用GC.Collect()手动不做任何事情。图像中显示的GC.Collect()是在内部完成的(我自己没有调用)。

Managed Memory Allocation

由于我没有将读取数据存储在任何地方,我希望在迭代DbDataReader时不会缓存数据。你能帮我理解这里发生了什么以及如何避免它吗?

c# odp.net
1个回答
0
投票

当使用带有托管驱动程序12.1.0.2的ExecuteReader()方法读取clob列值时,这似乎是一个已知错误(Bug 21975120)。解决方法是使用OracleDataReader特定方法(例如oracleDataReader.GetOracleValue(i))。可以显式关闭OracleClob值以释放内存分配。

var item = oracleDataReader.GetOracleValue(columnIndex);

if (item is OracleClob clob)
{
    if (clob != null)
    {
        // use clob.Value ...

        clob.Close();
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.