当命令参数为OracleDbType.TimeStamp时,Oracle.DataAccess(OraOps11w)中的内存泄漏

时间:2014-03-12 14:27:43

标签: c# oracle ado.net odp.net

当针对Oracle 11.2.0.3实例执行包含OracleTimeStamp的ADO.NET命令时,我注意到非托管内存一直在增长,直到使用了OracleConnection。到目前为止,我只使用OracleTimeStamp设法重现此行为,当使用其他类型(Varchar2,...)时,一切都按预期工作。

这是一个示例控制台应用程序,它导致非托管内存增长,直到连接被释放:

internal class MemoryTestOracle
{
    private const string SpTestOracleTimeStamp = "SP_MEM_TEST_TIMESTAMP";
    private const string SpTestOracleTimeStampParam = "P_CONTENT_DATETIMEOFFSET";
    private const int StatementsPerLoop = 1000;
    private const string ConnectionString =
        "Data Source=YOURSERVER;Persist Security Info=True;User ID=YOURUSER;Password=YOURPASSWORD;Pooling=false";

    private static void Main(string[] args)
    {
        using (OracleConnection connection = new OracleConnection(ConnectionString))
        {
            Console.WriteLine("Start connect with {0}", typeof(OracleConnection).Assembly.ToString());
            connection.Open();

            for (int currentLoop = 0; true; currentLoop++)
            {
                OracleTimeStamp[] valuesDateTimeOffset = new OracleTimeStamp[StatementsPerLoop];
                Parallel.For(0, StatementsPerLoop,
                    current => { valuesDateTimeOffset[current] = new OracleTimeStamp(DateTime.Now); });

                Stopwatch stopExecution = Stopwatch.StartNew();
                using (OracleTransaction transaction = connection.BeginTransaction())
                {
                    using (OracleCommand command = new OracleCommand(SpTestOracleTimeStamp, connection))
                    {
                        command.Transaction = transaction;
                        command.CommandType = CommandType.StoredProcedure;
                        using (OracleParameter timeStampParameter = new OracleParameter())
                        {
                            timeStampParameter.OracleDbType = OracleDbType.TimeStamp;
                            timeStampParameter.Value = valuesDateTimeOffset;
                            timeStampParameter.Direction = ParameterDirection.Input;
                            timeStampParameter.ParameterName = SpTestOracleTimeStampParam;

                            command.Parameters.Add(timeStampParameter);
                            command.ArrayBindCount = valuesDateTimeOffset.Length;
                            command.ExecuteNonQuery();
                        }
                    }
                    transaction.Commit();
                }
                stopExecution.Stop();
                Console.WriteLine("Loop # {0} took {1} ms", currentLoop, stopExecution.ElapsedMilliseconds);
            }
        }
    }
}

测试时我使用了以下storedProcedure:

CREATE OR REPLACE PROCEDURE SP_MEM_TEST_TIMESTAMP (
   P_CONTENT_DATETIMEOFFSET TIMESTAMP
   )
IS
BEGIN
   NULL;
END;
/

这是Oracle.DataAccess(4.112.3.0)中的错误,已安装的Oracle驱动程序还是我在这里做错了(最有可能,但我无法弄明白)?

非常感谢任何意见!

更新:添加使用/ disposing到OracleParameter本身。使用OracleParameter我认为我处理所有一次性物品。问题仍然存在。

更新2:在大约15.000次循环(15.000.000 db执行)之后,非托管内存大小约为150MB;即使DateTimeOffset值是在循环外创建的(所以基本上所有循环都在相同的数据上运行)内存不断增长(尽管速度较慢)

更新3:到目前为止,我已经使用Oracle的托管提供商测试了上述代码 - 没有内存问题。 我也用Devart的ADO提供商进行测试。在他们的一个版本(7. *)中,问题完全相同,在最近的版本中它似乎已修复。

1 个答案:

答案 0 :(得分:0)

尝试显式关闭和处理您正在使用的所有Oracle对象,包括OracleCommand,OracleTransaction和OracleParameter。

对于托管垃圾收集器,这些对象似乎并不重量级,但实际上它们可能在非托管端。因此,最好的做法是在完成任务后始终关闭并处理所有内容。