简体   繁体   English

返回一个小型DataTable时出现C#OutOfMemory异常

[英]C# OutOfMemory exception when returning a small DataTable

I have a SQLite database that has a single table with 18 million rows and 24 columns. 我有一个SQLite数据库,它有一个包含1800万行和24列的表。 I have written a SQL query function in C#, which I expose to Excel with ExcelDNA. 我在C#中编写了一个SQL查询函数,我用ExcelDNA将其暴露给Excel。

This is the complete code, 这是完整的代码,

    string constr = constr = "Data Source=" + FilePath + ";Version=3;Synchronous=OFF;temp_store=memory;cache_size=700000;count_changes=off;";
    DataTable dt = new DataTable();
    try
    {                                
        SQLiteConnection conn = new SQLiteConnection(constr);
        SQLiteCommand command = new SQLiteCommand(SQLStatement,conn);                
        conn.Open();                
        SQLiteDataAdapter sda = new SQLiteDataAdapter(command);                                
        sda.Fill(dt);
        sda.Dispose();
        command.Dispose();
        conn.Dispose();
        int numRows = (IncludeHeaders ? dt.Rows.Count + 1 : dt.Rows.Count);
        object[,] ret = new object[numRows, dt.Columns.Count];
        int rowCount = 0;
        if (IncludeHeaders)
        {
            int colCount = 0;
            foreach (DataColumn col in dt.Columns)
            {
                ret[rowCount, colCount] = col.ColumnName;
                colCount++;
            }
        }
        rowCount = (IncludeHeaders ? 1 : 0);
        foreach (DataRow row in dt.Rows)
        {
            int colCount = 0;
            foreach (DataColumn col in dt.Columns)
            {
                if (row[col] != DBNull.Value)
                    ret[rowCount, colCount] = row[col];
                else
                    ret[rowCount, colCount] = "";
                colCount++;
            }
            rowCount++;
        }
        return ret;
    }
    catch (Exception ex)
    {
        object[,] err = new object[1, 1];
        err[0, 0] = ex.ToString();
        return err;
    }
    finally
    {
        dt.Clear();
        dt.Dispose();
        dt = null;  
    }

If I run the query twice (two hits of Shift+F9), I get an OutOfMemoryException. 如果我运行两次查询(两次点击Shift + F9),我会得到一个OutOfMemoryException。 In the Task Manager I can see the Working Set(Memory) of the EXCEL.EXE image go from 200MB to 1500MB before the exception is thrown. 在任务管理器中,我可以看到EXCEL.EXE映像的工作集(内存)从抛出异常之前的200MB到1500MB。

However, this behavior isn't entirely consistent. 但是,这种行为并不完全一致。 Other queries where I return upto 5 columns and 1100 rows work just fine. 我返回5列和1100行的其他查询工作得很好。 I see the memory usage tick up in the Task Manager and once the results are returned to Excel I see the memory come back down. 我在任务管理器中看到了内存使用情况,一旦结果返回到Excel,我就会看到内存恢复正常。

Debugging the application above shows that it trips up at the sda.Fill(dt) line. 调试上面的应用程序表明它在sda.Fill(dt)sda.Fill(dt)

Would appreciate any thoughts? 会不会有任何想法? Would I be better off using SQLiteDataReader instead? 我会更好地使用SQLiteDataReader吗? Or are there any other tips or tricks I can use? 或者我可以使用任何其他提示或技巧? Thank you. 谢谢。

Incidentally if I run the exact query via Python I don't get this problem, so I'd assume it's something to do with the garbage collection in C#. 顺便说一句,如果我通过Python运行确切的查询我没有遇到这个问题,所以我认为它与C#中的垃圾收集有关。

Here are some details on the database and query. 以下是有关数据库和查询的一些详细信息。 The schema is along the lines of, 架构沿着,

Date (VARCHAR)
CompanyName (VARCHAR)
Amount (REAL)
AggCode (VARCHAR)
Level1 ... Level20 (VARCHAR)

The queries are usually run combining the fields Level9 , Level5 , AggCode , Date , CompanyName in the WHERE clause. 查询通常在WHERE子句中组合字段Level9Level5AggCodeDateCompanyName So apart from the raw table, I have also configured the following four indices, 所以除了原始表之外,我还配置了以下四个索引,

CREATE INDEX idx1 on my(Level09, AggCode);
CREATE INDEX idx2 on my(Level05, AggCode);
CREATE INDEX idx3 on my(CompanyName, AggCode);
CREATE INDEX idx4 on my(Date, AggCode);

The query that returns 1100 rows and 2 columns successfully, 成功返回1100行和2列的查询,

SELECT CompanyName, SUM(Amount) FROM my where Level09="T_EU" and AggCode = "R_S_V" GROUP BY CompanyName ORDER BY SUM(Amount) DESC

The query that throws the memory exception, 抛出内存异常的查询,

SELECT Date, CompanyName, Sum(Amount) FROM my WHERE Level05 ="M_TO" AND AggCode = "C_DTA" GROUP BY Date, CompanyName

The second query returns 163 rows and 3 columns in Python. 第二个查询在Python中返回163行和3列。

The full stack trace of the exception is below, 异常的完整堆栈跟踪如下,

System.Data.SQLite.SQLiteException (0x80004005): out of memory
out of memory
at System.Data.SQLite.SQLite3.Reset(SQLiteStatement stmt)
at System.Data.SQLite.SQLite3.Step(SQLiteStatement stmt)
at System.Data.SQLite.SQLiteDataReader.NextResult()
at System.Data.SQLite.SQLiteDataReader..ctor(SQLiteCommand cmd, CommandBehavior behave)
at System.Data.SQLite.SQLiteCommand.ExecuteReader(CommandBehavior behavior)
at System.Data.SQLite.SQLiteCommand.ExecuteDbDataReader(CommandBehavior behavior)
at System.Data.Common.DbCommand.System.Data.IDbCommand.ExecuteReader(CommandBehavior behavior)
at System.Data.Common.DbDataAdapter.FillInternal(DataSet dataset, DataTable[] datatables, Int32 startRecord, Int32 maxRecords, String srcTable, IDbCommand command, CommandBehavior behavior)
at System.Data.Common.DbDataAdapter.Fill(DataTable[] dataTables, Int32 startRecord, Int32 maxRecords, IDbCommand command, CommandBehavior behavior)
at System.Data.Common.DbDataAdapter.Fill(DataTable dataTable)
at UtilXL.Utils.UtilsSQLite.RunQueryCSLite(String SQLStatement, String FilePath, Boolean IncludeHeaders) in h:\Projects\UtilXL\UtilXL\Utils\UtilsSQLite.cs:line 37

Line 37 in referenced above is the sda.Fill() call. 上面引用的第37行是sda.Fill()调用。

If I use SqlDataReader then it falls over at the ExecuteReader() command, 如果我使用SqlDataReader那么它会在ExecuteReader()命令中出现,

System.Data.SQLite.SQLiteException (0x80004005): out of memory
at System.Data.SQLite.SQLite3.Reset(SQLiteStatement stmt)
at System.Data.SQLite.SQLite3.Step(SQLiteStatement stmt)
at System.Data.SQLite.SQLiteDataReader.NextResult() 
at System.Data.SQLite.SQLiteDataReader..ctor(SQLiteCommand cmd, CommandBehavior behave)
at System.Data.SQLite.SQLiteCommand.ExecuteReader(CommandBehavior behavior)
at System.Data.SQLite.SQLiteCommand.ExecuteReader()
at UtilXL.Utils.UtilsSQLite.RunQueryCSReader(String SQLStatement, String FilePath, Boolean IncludeHeaders) in h:\Projects\UtilXL\UtilXL\Utils\UtilsSQLite.cs:line 111

Your code does not have anything which can cause OutOfMemoryException. 您的代码没有任何可能导致OutOfMemoryException的内容。 So possibility is huge datatable and memory not getting recovered immediately. 因此可能是巨大的数据表和内存不能立即恢复。 One possibility is - As you are reading excel.. it may have blank rows at the end, which may potentially can increase dt size..... 一种可能性是 - 当你正在阅读excel时...它可能在末尾有空行,这可能会增加dt大小.....

As datatable is managed object calling dispose will not really help.. But at lease you can clear dt everytime once it use is over.. 由于数据表是托管对象调用dispose不会真正帮助..但是,一旦它使用结束,你可以每次清除dt ..

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

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