简体   繁体   English

Oracle ODP.NET游标泄漏?

[英]Oracle ODP.NET Cursor Leak?

I'm running into an open cursor limit issue with using the following code. 我正在使用以下代码遇到打开游标限制问题。 The open cursor limit on the oracle db is set to around 1000. The following code seems to hold onto the cursors even though I've got everything in a using statement (I think) that requires it. oracle db上的开放游标限制设置为大约1000.以下代码似乎保留在游标上,即使我已经在using声明(我认为)中的所有内容都需要它。 (Note, I don't need to read anything from outRefCursor2) (注意,我不需要从outRefCursor2读取任何内容)

Am I missing a using or some other clean up with ODP.net? 我错过了using ODP.net进行using或其他一些清理工作吗?
The exception occurs consistently at iteration 596. 异常在迭代596处发生。

static List<Thing> GetDetailsForItems(List<string> items) {
  DateTime start = DateTime.UtcNow;
  var things = new List<Thing>();
  var spname = "SP_GET_THING_DETAILS";
  var outRefCursorName1 = "p_ref_cursor1";
  var outRefCursorName2 = "p_ref_cursor2";

  // Create params
  var pInput1 = new OracleParameter("p_input1",
                  OracleDbType.Varchar2, ParameterDirection.Input);
  pInput1.Value = "";
  // Input 2 can be blank
  var pInput2 = new OracleParameter("p_input2",
                  OracleDbType.Varchar2, ParameterDirection.Input);
  pInput2.Value = "";

  var outRefCursor1 = new OracleParameter(outRefCursorName1,
                  OracleDbType.RefCursor, ParameterDirection.Output);
  var outRefCursor2 = new OracleParameter(outRefCursorName2,
                  OracleDbType.RefCursor, ParameterDirection.Output);

  int count = 0;
  using (var conn = new OracleConnection(CONN_STR)) {
    conn.Open();
    using (var cmd = conn.CreateCommand()) {
      cmd.Parameters.Add(pInput1);
      cmd.Parameters.Add(pInput2);
      cmd.Parameters.Add(outRefCursor1);
      cmd.Parameters.Add(outRefCursor2);
      cmd.CommandText = spname;
      cmd.CommandType = CommandType.StoredProcedure;
      foreach (string value in items) {
        count++; 
        cmd.Parameters[pInput1.ParameterName].Value = value;
        var execVal = cmd.ExecuteNonQuery();
        using (var refCursor = (Types.OracleRefCursor)
                                cmd.Parameters[outRefCursorName1].Value) {
          using (var reader = refCursor.GetDataReader()) {
            while (reader.Read()) {
              // read columns
              things.Add(reader["COLUMN_A"].ToString());
            }
          } // close reader
        } // close cursor
      } // end foreach
    } // close command
  } // close connection           
  int seconds = (DateTime.UtcNow - start).Seconds;
  Console.WriteLine("Finished in {0} seconds", seconds);
  return things;
}

I'm using this snippet found online to monitor DB cursors. 我正在使用在线发现的这个片段来监视数据库游标。 I can watch the cursors add up while stepping through the code. 我可以在逐步执行代码时看到游标加起来。 And they just keep adding at the cmd.ExecuteNonQuery() line. 他们只是继续添加cmd.ExecuteNonQuery()行。 I never see a drop after any using statement closes. 任何使用声明关闭后我都不会看到下降。

select sum(a.value) total_cur, avg(a.value) avg_cur, max(a.value) max_cur, 
s.username, s.machine
from v$sesstat a, v$statname b, v$session s 
where a.statistic# = b.statistic#  and s.sid=a.sid
and b.name = 'opened cursors current'
and machine='MY COMPUTER' 
group by s.username, s.machine
order by 1 desc;

Even though you're not using outRefCursor2 you still need to extract it and close it if it returns a valid cursor. 即使你没有使用outRefCursor2你仍然需要提取它并在它返回有效游标时关闭它。 ODP.net doesn't dispose of resources as nicely as the .Net version did so you need to make sure you dispose everything that is returned by ODP.net commands. ODP.net不像.Net版本那样处理资源,所以你需要确保你处理ODP.net命令返回的所有内容 As an extra step, it may not hurt to go and call .Close() explicitly on the cursors either to ensure that you're actually closing them (though the dispose should take care of that). 作为一个额外的步骤,可以在游标上明确地调用.Close()以确保你实际上关闭它们(尽管处理应该处理它)。

You need to dispose of the parameters: 您需要处理参数:

  • yes I confirmed this before showing native resource leaks (Virtual working set) when we didn't 是的,在我们没有显示本机资源泄漏(虚拟工作集)之前,我确认了这一点
  • I prefer to dispose of the params within the life-time of the connection so as to prevent any issues when the ref cursors need/want to use the connection on dispose (superstition, probably) 我更喜欢在连接的生命周期内处理参数,以便在ref refrsrs需要/想要使用处理时的连接时避免任何问题(可能是迷信)

    static List GetDetailsForItems(List items) { DateTime start = DateTime.UtcNow; static List GetDetailsForItems(List items){DateTime start = DateTime.UtcNow; var things = new List(); var things = new List(); var spname = "SP_GET_THING_DETAILS"; var spname =“SP_GET_THING_DETAILS”; var outRefCursorName1 = "p_ref_cursor1"; var outRefCursorName1 =“p_ref_cursor1”; var outRefCursorName2 = "p_ref_cursor2"; var outRefCursorName2 =“p_ref_cursor2”;

     try { int count = 0; using (var conn = new OracleConnection(CONN_STR)) try { conn.Open(); // Create params var pInput1 = new OracleParameter("p_input1", OracleDbType.Varchar2, ParameterDirection.Input); pInput1.Value = ""; // Input 2 can be blank var pInput2 = new OracleParameter("p_input2", OracleDbType.Varchar2, ParameterDirection.Input); pInput2.Value = ""; var outRefCursor1 = new OracleParameter(outRefCursorName1, OracleDbType.RefCursor, ParameterDirection.Output); var outRefCursor2 = new OracleParameter(outRefCursorName2, OracleDbType.RefCursor, ParameterDirection.Output); using (var cmd = conn.CreateCommand()) { cmd.Parameters.Add(pInput1); cmd.Parameters.Add(pInput2); cmd.Parameters.Add(outRefCursor1); cmd.Parameters.Add(outRefCursor2); cmd.CommandText = spname; cmd.CommandType = CommandType.StoredProcedure; foreach (string value in items) { count++; cmd.Parameters[pInput1.ParameterName].Value = value; var execVal = cmd.ExecuteNonQuery(); using (var refCursor = (Types.OracleRefCursor) cmd.Parameters[outRefCursorName1].Value) { using (var reader = refCursor.GetDataReader()) { while (reader.Read()) { // read columns things.Add(reader["COLUMN_A"].ToString()); } } // close reader } // close cursor } // end foreach } // close command } // close connection finally { pInput1.Dispose(); pInput2.Dispose(); outRefCursorName1.Dispose(); outRefCursorName2.Dispose(); } } int seconds = (DateTime.UtcNow - start).Seconds; Console.WriteLine("Finished in {0} seconds", seconds); return things; 

    } }

I wouldn't go for GC.collect()... It is an overkill... http://blogs.msdn.com/b/scottholden/archive/2004/12/28/339733.aspx 我不会去GC.collect()...这是一个矫枉过正的... http://blogs.msdn.com/b/scottholden/archive/2004/12/28/339733.aspx

But making sure disposing the command object worked for me. 但确保处理命令对象对我有用。 Easy is to use the "Using" 容易就是使用“使用”

Something like this: 像这样的东西:

using(DbCommand command = dbConn1.CreateCommand())
{
    command.CommandText = sql;
    using (var dataReader = command.ExecuteReader())
    {
        dbRows = ToList(dataReader);
    }
    mvarLastSQLError = 0;
}

None of the suggestions had worked thus far. 到目前为止,这些建议都没有奏效。 So in desperation, I ended up force GC collection every 200 iterations. 所以在绝望中,我最终每200次迭代强制GC收集。 With the following code. 使用以下代码。

if (count % 200 == 0) {
    GC.Collect();
}

What's strange is that when calling this method from a unit test, the manual GC.Collect() does not release any cursors. 奇怪的是,当从单元测试中调用此方法时,手册GC.Collect()不会释放任何游标。 But when calling the method from the business layer, it actually does work and I can see the open cursors get released by monitoring the oracle DB. 但是当从业务层调用该方法时,它确实可以正常工作,我可以看到通过监视oracle DB来释放打开的游标。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM