The .Net Concurrent BlockingCollection has a memory leak?
Asked Answered
A

1

8

I'm using a Producer/Consumer Pattern with a System.Collection.Concurrent.BlockingCollection<DataTable> to retrieve data from a database (producer) and create a Lucene Index on the data (consumer).

The Producer grabs 10000 records at a time and adds the set to the BlockingCollection<DataTable>. The consumer (which is a little bit slower) then grabs those 10000 and creates an index.

The blocking collection is bounded to 5 <DataTable> of 10000 rows each.

At first the program runs great but after its gets about 150000 rows in I noticed that my computers memory is maxed out and it slows to a crawl.

It seems that the BlockingCollection fails to set the underlying array slot to null after the item is taken.

Code:

    private static LuceneIndex index;
    private static BlockingCollection<DataTable> blockingCol;

    private static void Producer()
    {
        while (true)
        {
            //...get next 10000 rows
            DataTable data = GetNextSet();
            if(data.Row.Count > 0)
                blockingCol.Add(products);
            else
                break;
        }
    }

    private static void Consumer()
    {
        while (!BlockingCol.IsCompleted || BlockingCol.Count > 0)
        {
            DataTable data = blockingCol.Take();
            index.UpdateIndex(GetLuceneDocs(data));
        }
    }


 public static void Main(System.String[] args)
 {
            index = new LuceneIndex();
            blockingCol = new BlockingCollection<DataTable>(2);
            // Create the producer and consumer tasks.
            Task Prod = new Task(Producer);
            Task Con = new Task(Consumer);
            // Start the tasks.
            Con.Start();
            Prod.Start();
            // Wait for both to finish.
            try
            {
               Task.WaitAll(Con, Prod);
            }
            catch (AggregateException exc)
            {
               Console.WriteLine(exc);
            }
            finally
            {
               Con.Dispose();
               Prod.Dispose();
               blockingCol.Dispose();
             }
}

Can anyone confirm of reject this suspension? And is there any work around?

Anemochore answered 10/10, 2012 at 16:55 Comment(0)
D
8

Yes I can confirm this. You aren't on .NET 4.5, are you? It is supposed to be fixed there (and your comments under this answer seem to confirm this).

Anyway, write yourself a wrapper around a DataTable and clear that wrapper when you are done with the table. That makes it eligible for GC. The wrapper will not be GC'ed early but it is tiny.

class Wrapper<T> { public T Item; }
Dewey answered 10/10, 2012 at 17:2 Comment(4)
I am on .net 4.5. I'm actually using subsonic collection instead of datatable. I just included datatables in this example for simplicity. I'll try your solution tho.Anemochore
I guess it is not fixed then (probably I remembered). I have seen this problem myself, though.Dewey
Wrapper<DataTable> wrapper = BlockingCol.Take(); //do stuff wrapper.Item = null; This is what you mean right?Anemochore
NVM the server is not running 4.5. Must be the issue. You solution worked perfectly tho. I'll be using it until I can upgrade the server. thanksAnemochore

© 2022 - 2024 — McMap. All rights reserved.