How can a simple parameter save memory?

Cristiano Rodrigues
4 min readJan 12, 2022

--

It’s been a while since I’ve written anything, so I decided to start a small series on performance in .NET code.
And to debut the series I’m going to talk about the harmless List.

Analyzing the codes

We start with the code below.

The code is very simple and almost common in some snippets of some real-world applications. So let’s look at it from another angle, using WinDbg. Let’s take a Dump out of the process’s memory and analyze it a little more deeply.

After the generated dump, let’s load it in WinDbg and check how the stack is.

So far everything is normal. We have a reference from our list using our structure.

Let’s take a closer look.

Everything is alright! The items are in the list and we only have a single reference from our list.

So let’s look at the Heap.

Oops, where did these 10 MyStructure arrays come from? We didn’t create anything!

This is true, we don’t create anything, but we leave the list with dynamic size, that is, when including an item in the list an array of 4 elements will be created, and when completing 4 elements a new array with 8 elements will be created and the array of 4 elements will be “discarded”. Upon completing the 8 elements a new array with 16 elements will be created and the 8 element array will be “discarded”. With each creation of a new array, the data is “copied” from the old to the new, and new elements are added to the array. This process will be done until all the elements fit inside a single array.

Proving the existence of arrays.

Being the last array with 1024 elements.

These arrays will be discarded by the GC in the future and this can become a problem, increasing memory pressure and forcing the GC to run more often and GC running more often means the application will be “frozen” until the GC terminates your work (very summarized and simplified explanation).

So how can we avoid this? You don’t need much, just inform the number of elements that will be added to your list at the time of creation, through the list builder. Of course, we don’t always know the size! So that’s why it’s good to understand the data/business to get an approximate volumetry and be able to initialize the list to avoid that the creation process starts with 4 elements. You don’t have to be precise, but if you can get closer or even lessen the amount of array creation, it’s already a great benefit.

In our case, we will inform through the class constructor a capacity of 1000 elements. At this point, an array of 1000 elements will be allocated enough for our example.

Collecting a new dump and analyzing the Heap.

See that we now have just one instance of an array of MyStructure. So let’s take a closer look at the object.

Based on the result we can confirm that we have a single array of 1000 elements, that is, we do not “drop” objects to be discarded by the GC in the future.

Comparing code efficiency

Based on the result, the method that initializes the list with a capacity of 1000 elements runs faster and allocates less memory.

Conclusion

It’s the little things that make a difference in the end. That’s why it’s important to understand how they work. In our case, a small change in the capacity of elements during the construction of a simple list showed a considerable gain, imagine taking this to real life codes! What would your gain be?

Don’t forget to measure, measure and measure again to compare the old code with the new one.

It is with data that we prove the performance gain!

To the next!

--

--

Cristiano Rodrigues
Cristiano Rodrigues

Written by Cristiano Rodrigues

Microsoft MVP | Solutions Architect with more than 15 years in software development. In love for Docker and Kubernetes, a specialist in .NET and SQL Server.

No responses yet