Oracle.ManagedDataAccess 库中可能存在内存泄漏
Possible Memory Leak In Oracle.ManagedDataAccess Library
我开始编写一个程序,将数据从 oracle 数据库复制到 SQL 服务器数据库,并立即 运行 进入内存问题。我正在使用 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() 方法来重置 DataTable)。
为什么会发生这种情况(我可以做些什么来解决它)?
感谢您的帮助。
编辑:
我在 Oracle 中的测试 table 中有一个 CLOB 列。该问题似乎与读取该 CLOB 值有关,因为使用其他 tables(无 OutOfMemoryException)时该问题不会出现。是否有我应该使用的用于访问 Oracle 的更好的库?
编辑 2:
我还应该提到我正在测试的 table(带有 CLOB 列的那个)有大约 290 万条记录,它通常在第 500,000 行和第 1,500,000 行之间的某个地方失败(内存不足之前的实际最低行数失败是约64.9万,最高约139万)。
编辑 3:
我尝试将这段代码配对以帮助识别问题,其中有区别的一行是:
int valueCount = reader.GetValues(currentRowValues);
我还尝试了一次只读取一列的版本,在这种情况下,使用索引读取值会导致问题(仅在 CLOB 列上)。这是导致异常的替代版本中的行:
newRow[columnIndex] = reader[columnIndex];
根据 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();
}
}
我在批量读取 300 万行时遇到了与 CLOB 列相同的问题application.When我将 InitialLobFetchSize 设置为 -1 它似乎可以控制内存使用:
command.InitialLOBFetchSize = -1;
我开始编写一个程序,将数据从 oracle 数据库复制到 SQL 服务器数据库,并立即 运行 进入内存问题。我正在使用 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() 方法来重置 DataTable)。
为什么会发生这种情况(我可以做些什么来解决它)?
感谢您的帮助。
编辑: 我在 Oracle 中的测试 table 中有一个 CLOB 列。该问题似乎与读取该 CLOB 值有关,因为使用其他 tables(无 OutOfMemoryException)时该问题不会出现。是否有我应该使用的用于访问 Oracle 的更好的库?
编辑 2: 我还应该提到我正在测试的 table(带有 CLOB 列的那个)有大约 290 万条记录,它通常在第 500,000 行和第 1,500,000 行之间的某个地方失败(内存不足之前的实际最低行数失败是约64.9万,最高约139万)。
编辑 3: 我尝试将这段代码配对以帮助识别问题,其中有区别的一行是:
int valueCount = reader.GetValues(currentRowValues);
我还尝试了一次只读取一列的版本,在这种情况下,使用索引读取值会导致问题(仅在 CLOB 列上)。这是导致异常的替代版本中的行:
newRow[columnIndex] = reader[columnIndex];
根据 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();
}
}
我在批量读取 300 万行时遇到了与 CLOB 列相同的问题application.When我将 InitialLobFetchSize 设置为 -1 它似乎可以控制内存使用:
command.InitialLOBFetchSize = -1;