Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why does the memory usage grows after each page load of same page

I have a simple view to show around 2000 customers (not yet paged, which is the next step in my process).

This is my controller part:

public class CustomersController : Controller
{
    private readonly MyModel _context;

    public CustomersController(MyModel context)
    {
        _context = context;
    }
 // GET: Customers
    public async Task<IActionResult> Index()
    {
        var customers = await _context.Customers.ToListAsync();
        return View(customers);
    }

}

and a simple view:

@model IEnumerable<Customer>

  @{ ViewData["Title"] = "Index"; }

  <table class="table">
    <thead>
      <tr>
        <th>
          @Html.DisplayNameFor(model => model.Name)
        </th>
        <th></th>
      </tr>
    </thead>
    <tbody>
      @foreach (var item in Model) {
      <tr>
        <td>
          @Html.DisplayFor(modelItem => item.Name)
        </td>
      </tr>
      }
    </tbody>
  </table>

Whenever I refresh the page (returning the same resultset), the process memory grows with 20 to 30 MB each time.

Below a screenshot of the memory diagnostics: snapshot 1 is before the first page load; snapshot 2 is after the first page load; snapshot 3 is after tens of reloads of that same page...

memory diagnostic

I would think that the memory would stay the same, because the DbContext should be disposed of at each request, no?

What am I doing wrong and how can I control the memory usage?

[EDIT]

So, it appears that GC should kick in after a while. This screenshot shows it: part before first GC is a lot of consecutive loads of the same page (Customers). between the first and the second GC another page (Products) was loaded multiple times (not causing any memory rise?). After the third GC memory drops a bit, but is still "freigthening" high IMHO...

enter image description here

like image 497
intrixius Avatar asked Sep 20 '25 20:09

intrixius


2 Answers

I think you have some misconceptions on what the garbage collectors purpose is. It's purpose is not to minimize memory usage; rather, it's to ensure your process has memory available. It's an important distinction.

The garbage collector will periodically sweep memory for objects that are no longer in use and mark them for collection. Later, when the GC detects that it is running low on memory, it will activate and:

  1. Move things around, so you have fewer small blocks of memory and more larger blocks of memory (memory can only be allocated in contiguous blocks).

  2. Collect objects and free up their memory.

It will run infrequently. This can be configured to tailor the GC to specific requirements, though this is rarely required. Each time the GC runs, it will do as little as possible to ensure your program has the memory it needs. It does this because garbage collection is expensive, and can result in noticeable performance spikes.

Disposing an object does not mean "immediately get rid of this and free up its memory". It means "I'm not using this anymore, so you can clean it up at your convenience." (software is so polite!) So, disposing of your context won't result in an immediate reduction of memory usage. It is possible to give a very strong hint to the GC that you want a collection, but its rarely needed and not advised.

So, your GC will wait. It will bide its time until it detects "hey, we're running low on memory!" Then, and only then, will it inspect your process for unreachable objects for collection.

So, you can expect the GC to run when your memory consumption begins to exceed the memory available to the process. Then, it will free memory as quickly as possible. It's goal is only to ensure your process has memory available.

So, what can you expect to observe?

  1. Your processes memory will go up and down like a roller coaster. It will go up more frequently than it goes down.

  2. Your process memory usage will trend toward the maximum available memory, and will generally only go down as it approaches the limit.

  3. Collections will be infrequent (as needed) and can vary wildly in size.

I do not think you have anything to be concerned about, and recommend you only worry about your memory usage if you start getting out-of-memory errors.

If you would like to know how some parts of your code really behave, I recommend you to use https://github.com/dotnet/BenchmarkDotNet You can actually see how memory usage and GC collections will look like with MemoryDiagnoser. I recommend to use GcMode Server to be TRUE as it will show you better how GC will work on server and not on local workstation. I know it is a hussle to learn how to use it, but it is well worth after some time.

Trouble with looking at the memory usage in VS is because it is in Debug mode and that is not exactly how your code will behave when you Release it.

like image 28
Nick43 Avatar answered Sep 22 '25 11:09

Nick43