【问题标题】:Possible Memory Leak In Oracle.ManagedDataAccess LibraryOracle.ManagedDataAccess 库中可能存在内存泄漏
【发布时间】:2018-01-26 04:14:04
【问题描述】:

我开始编写一个程序,将数据从 oracle 数据库复制到 SQL Server 数据库,并立即遇到了内存问题。我正在使用 Oracle.ManagedDataAccess 库(nuget 安装命令:“install-package Oracle.ManagedDataAccess”)。该库是版本 4.122.1.0

这是我的功能:

    private static void LoadTable(TableToLoad table)
    {
        DataTable loadBuffer = null;
        //assume source is oracle for now.  assume destination is sql server
        using (OracleConnection conn = new OracleConnection(table.SourceConnectionString))
        {
            OracleDataReader reader = OracleCommands.GetDataReader(string.Format("select * from \"{0}\".\"{1}\"", table.SourceSchema, table.SourceTable),conn);
            bool foundData = reader.Read();
            if (loadBuffer == null)
            {
                loadBuffer = InitializeBuffer(reader);
            }

            int recordsAffected;

            while (foundData==true)
            {
                object[] currentRowValues = new object[reader.FieldCount];
                int valueCount = reader.GetValues(currentRowValues);
                loadBuffer.Rows.Add(currentRowValues);
                if (loadBuffer.Rows.Count >= 15000)
                {
                    SqlCommands.RunSqlCommandWithDataTableInput(string.Format("insert into {0}.{1} select * from @loadBufferTable", table.TargetSchema, table.TargetTable), table.TargetConnectionString, out recordsAffected, loadBuffer, "loadBufferTable");

                    loadBuffer.Dispose();
                    loadBuffer = null;
                    loadBuffer = InitializeBuffer(reader);
                }
                foundData = reader.Read();
            }

            if(loadBuffer.Rows.Count>0)
            {
                SqlCommands.RunSqlCommandWithDataTableInput(string.Format("insert into {0}.{1} select * from @loadBufferTable", table.TargetSchema, table.TargetTable), table.TargetConnectionString, out recordsAffected, loadBuffer, "loadBufferTable");

                loadBuffer.Dispose();
                loadBuffer = null;
            }

            reader.Close();
            reader.Dispose();
            reader = null;

        }
    }

当我运行此程序时,内存消耗会猛增,几分钟后我会收到内存不足错误。我停止了这个过程,并使用诊断工具来查看是什么占用了这么多内存。几乎所有内存都被数万个由 DataReader.Read 方法创建的“OraBuf”对象占用。

我尝试取消分配并重新创建我用作插入批处理的缓冲区的 DataTable 对象,我认为 DataTable 可能以某种方式持有对 OraBuf 对象的引用,但这并没有解决问题(我最初使用的是 DataTable .Clear() 方法来重置数据表)。

为什么会发生这种情况(我可以做些什么来解决它)?

感谢您的帮助。

编辑: 我在 Oracle 中的测试表中有一个 CLOB 列。该问题似乎与读取该 CLOB 值有关,因为在使用其他表时问题不会出现(没有 OutOfMemoryException)。是否有更好的库来访问我应该使用的 Oracle?

编辑 2: 我还应该提到我正在测试的表(带有 CLOB 列的表)有大约 290 万条记录,它通常在第 500,000 行和第 1,500,000 行之间失败(在内存不足失败之前的实际最低行数约为 649,000 和最高约为 1,390,000)。

编辑 3: 我尝试将此代码配对以帮助识别问题,而产生影响的一行是:

    int valueCount = reader.GetValues(currentRowValues);

我还尝试了一次只读取一列的版本,在这种情况下,使用索引读取值会导致问题(仅在 CLOB 列上)。这是导致异常的替代版本中的行:

    newRow[columnIndex] = reader[columnIndex];

【问题讨论】:

  • 这是一份一次性工作吗?是的,那么也许您应该考虑另一种方法,例如DB-Link 或通过 CSV 文件传输。
  • 你试过loadBuffer.Clear();而不是丢弃它吗?
  • 感谢您对此的看法。我试过 loadBuffer.Clear() 但这没有帮助。
  • 这是我们希望定期运行的工作。我们之前已经完成了与 SSIS 包(数百个)的数据同步,但我们正在尝试构建这个更易于使用的解决方案(同步新表时,我们只需将其添加到同步列表/表中)。

标签: .net oracle memory memory-leaks oledb


【解决方案1】:

在批处理应用程序中读取 300 万行时,我遇到了与 CLOB 列相同的问题。当我将 InitialLobFetchSize 设置为 -1 时,它似乎可以控制内存使用:

command.InitialLOBFetchSize = -1;

【讨论】:

  • 感谢您的回答,很抱歉这么久没看。我们最终通过回到较旧的 OLEDB 驱动程序版本解决了这个问题(当时最新的 3 或 4 个驱动程序版本存在此错误,但在此之前有一个版本支持我们正在使用的数据库版本和功能)。对不起,我不记得我头顶上的确切版本。这些修复很棒,但问题也以其他方式表现出来(例如在 SSIS 包中),因此使用旧驱动程序是当时对我们来说最好的方法。甲骨文解决了这个问题吗?
【解决方案2】:

根据link,这是Oracle驱动程序中的一个LOB处理错误。

解决方法(取自here)是:

                    var dataValue = reader.GetOracleValue(i);  

                    if (dataValue != null)  
                    {  
                        // Test returned Type  
                        if (dataValue is OracleClob)  
                        {  
                            OracleClob oClob = dataValue as OracleClob;  
                            // Read text  
                            currentRowValues[i] = oClob.Value;  
                            oClob?.Close();  
                        }  
                        else if (dataValue is OracleBlob)  
                        {  
                            OracleBlob oBlob = dataValue as OracleBlob;  
                            // Read data  
                            currentRowValues[i] = oBlob.Value;  
                            oBlob?.Close();  
                        }  
                    }

【讨论】:

    猜你喜欢
    • 2011-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-23
    • 2020-06-12
    • 1970-01-01
    • 1970-01-01
    • 2011-01-03
    相关资源
    最近更新 更多