top of page

How you should easily prevent this memory leak with EF Core

Entity Framework core, as the most used .net core ORM (object-relational mapping) framework, simplify the developer’s life by managing its database access complexity.

However, as it usually happens, such simplification comes with a price. In most cases, the price is flexibility and performance.


1.Simplicity is a trap

Injecting the database context in the built-in IoC container is as quick as the following:


The TodoContext becomes then accessible from anywhere needed in you application, to create, read, update or delete any data from your database provider.

The issue arises quickly when you have a different need than responding to an Http request, a background worker for example:

Background job containing a memory leak


If you don’t have enough knowledge of ASP.NET core and of the behaviors of EFcore, the leak is hard to spot:

  • DBContext objects tracking: during the lifetime of the context, all the manipulated entities are kept and tracked by default in the context object.

  • Dependency Injection scope: Each http request triggers the creation of a scope to resolve dependencies from. At the end of the request, the scope and the related dependencies will be disposed.

In definitive, we are here creating a memory leak by using a context that will keep growing in size through the whole application lifetime

2. Easy solution

Once spotted, the leak is easy to fix: DBContext should have a short life span!

We have 2 solutions here, either configure the ORM to avoid tacking entities or manually reduce the context lifetime.

Our solution here is, for each loop, to request a new DBContext that will be disposed at the end of the current iteration.

Reducing the DBContext lifetime


3. Is it that bad ?

Let’s find out ! Using benchmarkdotnet will show us the execution time, the garbage collector pressure and the memory footprint for our 2 cases:

Method Loop Mean Gen 0 Gen 1 Gen 2 Allocated

Work 100 465.8 ms 2000.0000 - - 39 MB

Work 500 4,438.0 ms 58000.0000 1000.0000 - 914 MB

Work 1000 13,344.0 ms 231000.0000 7000.0000 - 3,631 MB

Work 5000 223,459.5 ms 5515000.0000 99000.0000 48000.0000 90,249 MB

Work 10000 787,672.9 ms 21847000.0000 279000.0000 192000.0000 360,742 MB



Method Loop Mean Gen 0 Gen 1 Allocated

Work 100 434.6 ms - - 10 MB

Work 500 1,927.2 ms 3000.0000 - 50 MB

Work 1000 3,828.6 ms 6000.0000 - 100 MB

Work 5000 19,264.2 ms 30000.0000 1000.0000 502 MB

Work 10000 38,891.4 ms 61000.0000 - 1,004 MB


In the worst case, our short life span context solution is 20 times faster and use 360 times less memory!



Conclusion

Even though Entity Framework core is an easy ORM to set up and to work with, some internal knowledge are necessary to avoid some pitfalls.

The DBContext is a powerful tool when it comes to managing business transactions and avoiding concurrency collisions, but its lifetime should be supervised. Otherwise the performance of your application(execution speed and memory usage) might deteriorate drastically.



Source: Medium - Royer Robin


The Tech Platform

0 comments
bottom of page