How can I free-up memory used by a Parallel.Task?

前端 未结 2 2145
无人共我
无人共我 2020-12-16 21:34

I have a program that does a memory intensive simulation. Below I\'ve written a small console application that replicates the problem I\'m having.

class Prog         


        
相关标签:
2条回答
  • 2020-12-16 21:51

    I'm sure you don't have a problem with your memory, .NET just doesn't shrink the used Memory so it can be assigend in the future. This saves time for future memory allocation. Try to rerun the loop after it finished, i'm sure the memory wouldn't grow.

    So please just try this, i would be intrested in the outcome!

    class Program {
        static void Main(string[] args) {
            Process currentProcess = Process.GetCurrentProcess();
            for (int i = 0; i < 10; i++) {
                var t = new Task(() => DoMemoryHog(20000000));
                t.Start();
                t.Wait();
                t.Dispose();
                t = null;
                GC.Collect();
                Console.WriteLine("Done" +i);
                Console.WriteLine("Memory: " + GC.GetTotalMemory(false));
                Console.WriteLine("Paged: " + currentProcess.PagedMemorySize64);
                Console.WriteLine("-------------------------");
            }
            Console.ReadLine();
        }
    
        static void DoMemoryHog(int n) {
            ConcurrentBag<double> results = new ConcurrentBag<double>();
    
            Parallel.For(0, n, (i) =>
            {
                results.Add(Math.Sqrt(i.GetHashCode()));
            });
        }
    }
    

    Those two methods are used to print out memory usage:

    GC.GetTotalMemory();
    currentProcess.PagedMemorySize64;
    

    My output is:

    Done0
    Memory: 480080992
    Paged: 520753152
    -------------------------
    Done1
    Memory: 480081512
    Paged: 520753152
    -------------------------
    Done2
    Memory: 480082160
    Paged: 520753152
    -------------------------
    Done3
    Memory: 480083476
    Paged: 520753152
    -------------------------
    Done4
    Memory: 480083496
    Paged: 520753152
    -------------------------
    Done5
    Memory: 480083516
    Paged: 520753152
    -------------------------
    Done6
    Memory: 480083536
    Paged: 520753152
    -------------------------
    Done7
    Memory: 480084204
    Paged: 520753152
    -------------------------
    Done8
    Memory: 480084204
    Paged: 520753152
    -------------------------
    Done9
    Memory: 480085500
    Paged: 520753152
    -------------------------
    

    As far as i can see there is no memory problem here. The objects are cleand up and the memory is reused propery. Issue solved or?

    Update

    Private bytes behavior:

    PrivateBytes of application

    As you can see the GC is collecting the Objects and freeing the memory, but it's currently not releasing it, so new objects can be allocated.

    0 讨论(0)
  • 2020-12-16 21:59

    This is due to the nature of a concurrentBag (see my earlier question regarding ConcurrentBag ( Possible memoryleak in ConcurrentBag? )).

    Basically, a concurrent bag stores items on the local thread. If you dont consume the items, the items will stay on the local thread. Please check the following example:

        class TrackedItem
        {
            public TrackedItem()
            {
                Console.WriteLine("Constructor!");
            }
            ~TrackedItem()
            {
                Console.WriteLine("Destructor!");
            }
        }
    
        static void Main(string[] args)
        {
            Action allCollect = () =>
                {
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                    GC.Collect();
                };
    
            // create a single item and loose it directly thereafter
            TrackedItem item = new TrackedItem();
            item = null;
            allCollect();
            // Constructor!, Destructor!
    
            ConcurrentBag<TrackedItem> bag = new ConcurrentBag<TrackedItem>();
            bag.Add(new TrackedItem());
            bag = null;
            allCollect();
            // Constructor!
            // Note that the destructor was not called since there is still a collection on the local thread
    
            Console.ReadLine();
        }
    

    The concurrentBag makes use of the ThreadLocal class which makes it convenient to hold 1 instance per thread. The intended way for disposing data in a ThreadLocal is by calling the Dispose method on ThreadLocal (ThreadLocal implements IDisposable). This disposes only the data for the current thread. The concurrentBag doesn't dispose it's ThreadLocals though. Instead it relies on all items being consumed- or a thread hosting the ThreadLocal being disposed. this can be very nasty however when you share threads like within a ThreadPool.

    0 讨论(0)
提交回复
热议问题