从文件读取时内存泄漏

时间:2015-03-03 20:06:56

标签: c# memory-leaks out-of-memory

我有这个方法从.dbf文件中读取:

public DataTable ReadBulkDBF(string dbfFile, Dictionary<string, string> columnKeys, int maxRows, string dynamicValue, int nextId)
{
    long start = DateTime.Now.Ticks;
    DataTable dt = new DataTable();
    BinaryReader recReader;
    string number;
    string year;
    string month;
    string day;
    long lDate;
    long lTime;
    DataRow row;
    int fieldIndex;
    bool foundLastColumn = false;
    List<string> keys = new List<string>(columnKeys.Keys);
    List<string> values = new List<string>(columnKeys.Values);

    // For testing purposes
    int rowCount = 0;
    // If there isn't even a file, just return an empty DataTable
    if ((!File.Exists(dbfFile)))
    {
        return dt;
    }

    BinaryReader br = null;
    try
    {
        // Will allow shared open as long as the other application using it allows it too.
        // Read the header into a buffer
        br = new BinaryReader(File.Open(dbfFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite));
        byte[] buffer = br.ReadBytes(Marshal.SizeOf(typeof(DBFHeader)));

        // Marshall the header into a DBFHeader structure
        GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
        DBFHeader header = (DBFHeader)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(DBFHeader));
        handle.Free();

        // Read in all the field descriptors. Per the spec, 13 (0D) marks the end of the field descriptors
        ArrayList fields = new ArrayList();
        while ((13 != br.PeekChar()))
        {
            buffer = br.ReadBytes(Marshal.SizeOf(typeof(FieldDescriptor)));
            handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
            fields.Add((FieldDescriptor)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(FieldDescriptor)));
            handle.Free();
        }

        // Read in the first row of records, we need this to help determine column types below
        ((FileStream)br.BaseStream).Seek(header.headerLen + 1, SeekOrigin.Begin);
        buffer = br.ReadBytes(header.recordLen);
        recReader = new BinaryReader(new MemoryStream(buffer));

        // Create the columns in our new DataTable
        DataColumn col = null;

        dt.Columns.Add(new DataColumn("updateId", typeof(int)));
        if (!dbfFile.Contains("con_compania")) { dt.Columns.Add(new DataColumn("dynamic", typeof(string))); }
        dt.Columns.Add(new DataColumn("fechasync", typeof(DateTime)));

        foreach (FieldDescriptor field in fields)
        {
            // Adds columns to DataTable dt
        }

        // Skip past the end of the header. 
        ((FileStream)br.BaseStream).Seek(header.headerLen, SeekOrigin.Begin);

        // Read in all the records
        for (int counter = 0; counter < header.numRecords && dt.Rows.Count < maxRows; counter++)
        {
            // First we'll read the entire record into a buffer and then read each field from the buffer
            // This helps account for any extra space at the end of each record and probably performs better
            buffer = br.ReadBytes(header.recordLen);
            recReader = new BinaryReader(new MemoryStream(buffer));

            // All dbf field records begin with a deleted flag field. Deleted - 0x2A (asterisk) else 0x20 (space)
            if (recReader.ReadChar() == '*')
            {
                continue;
            }

            // Loop through each field in a record
            fieldIndex = 2;


            rowCount = dt.Rows.Count;
            row = dt.NewRow();

    foreach (FieldDescriptor field in fields)
            {
                switch (field.fieldType)
                {
                    // Casts field's value according to its type and saves it in the dt.

                }
                fieldIndex++;
            }
            // Looks for key-value combination in every row until 
            // it finds it to know where to start reading the new rows.
            if (!foundLastColumn && columnKeys.Keys.Count > 0)
            {
                foundLastColumn = true;

                int i = 3;

                if (dbfFile.Contains("con_compania")) { i = 2; }

                for (; i < keys.Count && foundLastColumn; i++)
                {
                    if (!row[keys[i]].ToString().Equals(values[i]))
                    {
                        foundLastColumn = false;
                    }
                }
            }
            else
            {
                dt.Rows.Add(row);
                nextId++;
            }

        }
    }
    catch (Exception e)
    {
        throw e;
    }
    finally
    {
        if (null != br)
        {
            br.Close();
            br.Dispose();
        }
    }

    long count = DateTime.Now.Ticks - start;

    return dt;
}

问题出在某个地方我会留下某种参考,所以我得到了OOM。

使用类似的方法调用该方法:

DataTable dt = new ParseDBF().ReadBulkDBF(...);
//Use dt
dt.Dispose();
dt = null;

如果我只调用Dispose()它会保留引用,如果我调用null dt变为null,但是对ParseDBF对象的引用仍然存在。

知道泄漏可能在哪里?我已经在互联网上寻找想法,并尝试调用Dispose()和Close(),并设置为null我使用它之后我能想到的一切,并且它一直在发生。

1 个答案:

答案 0 :(得分:1)

我注意到重新加载者可能没有被释放。

我强烈建议在此代码中使用块来确保在执行离开使用范围时清除IDisposable对象。