none
Memory keeps allocated after method ends

    Question

  • I'm developing a Windows Service with C# and .NET Framework 4.6.2 and Entity Framework 6.1.3.

    I have a Web API interface on that service and in one Controller's method I create a Dictionary with more than one million strings:   
    Dictionary<string, List<string>> codes;
    This Dictionary is created inside another method:   
    codes = GetCodesAsDictionary(connectionString, productionId);
    I pass this variable to a static method from a static class.

    One Visual Studio 2015's memory profiler I see how memory grows to 1,1Gb when I call this method, but when the method ends, the memory keep as 1,1Gb.

    If I call the method again, memory grows again to 2Gb, and after method finishes, memory goes to 1,1Gb again.

    I have tried to pass the `codes` variable as reference, but I get the same memory allocation. I have to stop the service and re-run it again to release that 1,1Gb. By the way, I pass it by reference because I don't want that the static method makes a copy of it.

    Do you know why it doesn't release such amount of memory after method ends?

    UPDATE:

    I have changed the static class with an instance class (copying the method from static class) and I get the same result (1,1Gb memory allocated).

    SECOND UPDATE:

    I have also changed how the dictionary is created:

    Dictionary<string, List<string>> codes = null;

    GetCodesAsDictionary(connectionString, productionId, out codes);

    And I get the same result: 1,1Gb memory allocated.
    Monday, April 3, 2017 6:12 AM

All replies

  • That does not sound unusual to me.  When you allocate a lot of memory, the managed heap will grow.  After GC runs the heap may not shrink.  It stays large and empty on the assumption that you will need that much memory again.  

    see

    "To reserve memory, the garbage collector calls the Win32 VirtualAlloc function, and reserves one segment of memory at a time for managed applications. The garbage collector also reserves segments as needed, and releases segments back to the operating system (after clearing them of any objects) by calling the Win32 VirtualFree function."

    Fundamentals of Garbage Collection

    David


    Microsoft Technology Center - Dallas
    My blog


    Monday, April 3, 2017 2:36 PM
  • The Garbage Collector can't release the memory as long as codes is in scope. If you call the method to get the dictionary a second time, there will be two dictionaries in scope while the method is running, but only one after it returns.

    Dictionary<string, List<string>> codes;
    
    // After this call, we will have one Gigabyte allocated.
    codes = GetCodesAsDictionary(connectionString, productionId);
    
    // During this call, we will have another gigabyte (so 2 Gig total).
    // But after the call is over, the original value of codes 
    // will be lost, so the GC can free up one Gig. We will still
    // have one Gig (the second one).
    codes = GetCodesAsDictionary(connectionString, productionId);
    
    // If we want to free up all the memory, codes must go out 
    // of scope. Or we can set it to null.
    codes = null;


    Monday, April 3, 2017 11:45 PM
  • The Garbage Collector can't release the memory as long as codes is in scope. If you call the method to get the dictionary a second time, there will be two dictionaries in scope while the method is running, but only one after it returns.

    Dictionary<string, List<string>> codes;
    
    // After this call, we will have one Gigabyte allocated.
    codes = GetCodesAsDictionary(connectionString, productionId);
    
    // During this call, we will have another gigabyte (so 2 Gig total).
    // But after the call is over, the original value of codes 
    // will be lost, so the GC can free up one Gig. We will still
    // have one Gig (the second one).
    codes = GetCodesAsDictionary(connectionString, productionId);
    
    // If we want to free up all the memory, codes must go out 
    // of scope. Or we can set it to null.
    codes = null;


    Thanks but ALL variables are declared inside their methods, and I only used to pass them as parameter for another methods. I don't use any global variable and all the methods end their execution.

    Therefore, the GC can release them because they are out of scope.

    Tuesday, April 4, 2017 6:12 AM
  • And what is the problem. That there is memory allocated is not a problem on itself. It becomes a problem as other programs go telling that there is short of memory. 

    The GC is not a kind of engine which is all the time using the cores of your computer to clean up memory. But runs when there is nothing else to do or when there is short of memory. 


    Success
    Cor

    Tuesday, April 4, 2017 6:28 AM
  • I have reduce the amount of memory used to 250Mb only changing the Entity Framework Select. My error here was that I retrieve the entire table and now I retrieve only the two columns that I need.

    I want to show you the memory profile window (or whatever is called):

    The snapshots are:

    1. The first one after restart the service and attach the Visual Studio debugger to it.

    2. The next sentence after method that retrieve the codes from database returns them.

    3. When everything ends.

    I think the GC has done its job and, as davidbaxterbrownehas said, the memory keeps reserved.

    Tuesday, April 4, 2017 6:51 AM
  • I've made a second call to the same method and I've seen this:

    Memory allocated is released before it retrieves the codes again from the database.

    Tuesday, April 4, 2017 7:04 AM