C# Memory Usage for Arrays of Objects

Test the memory usage of arrays of objects. Using separate arrays can improve performance.

Memory. We improve the memory usage in a program by modifying the storage of large collections. By using CLRProfiler, we gauge memory. We test the memory usage of classes and value types.

First example. The program here has 2 classes that contain object references. The program tests 3 separate arrays of objects against an array of classes with those same values.
TestArray1: This contains only 3 object references, for 3 arrays. The array variables such as string[] are only references.
TestArray2: This contains an array of classes. Each class in the array has the same 3 value types.
Info: The string reference actually stores no data unless assigned. Integers and DateTimes are stored inline.
StringsInt, uintDateTime
Main: This method has 2 paths, which you can change by turning the "true" Boolean literal to "false." It uses TestArray1 or TestArray2.
Tools used: CLRProfiler, 32-bit Windows C# program that measures memory using System; class TestArray1 { public string[] _value1; // Array of string references public int[] _value2; // Array of integers public DateTime[] _value3; // Array of structs } class TestArray2 { public TestClass1[] _class1; // Array of classes } class TestClass1 { public string _value1; // String reference public int _value2; // Integer public DateTime _value3; // Struct } class Program { const int _constant = 10000000; // <-- Number of elements required static void Main() { // True will test the arrays, false will check the classes. if (true) { // // This tests the memory usage with separate arrays. // TestArray1 test1 = new TestArray1(); // Allocate each array. test1._value1 = new string[_constant]; test1._value2 = new int[_constant]; test1._value3 = new DateTime[_constant]; } else { // // This tests the memory usage of array of classes. // TestArray2 test2 = new TestArray2(); test2._class1 = new TestClass1[_constant]; for (int i = 0; i < test2._class1.Length; i++) { // Allocate each class. test2._class1[i] = new TestClass1(); } } } }

CLRProfiler. The CLRProfiler application is developed by Microsoft. It provides an accurate look into your application's memory usage, although it does affect performance. When you download the CLRProfiler, extract it to your "C:\" directory.
Then: Browse to the x86 folder, and double-click on CLRProfiler.exe. The program will start.

The profiler dialog box will appear at this point. Click on the "Start Application" button and then select the Release binary for your program (such as ConsoleApplication461.exe) that is in the Visual Studio Projects directory.

Your application will execute. If the program is too large, it will be slow. You may need to reduce its allocations first. Next, you will see the Summary dialog. Click on the buttons and explore the graphs and histograms.
Tip: I usually just take screenshots of the charts instead of saving the files, because I have organization problems.

Results. Here we look again at the example program and inspect the two snapshots from CLRProfiler I took when executing the programs in CLRProfiler. First, here is the benchmark for TestArray1 (which is in the first path of the Main entry point).
Measurements Total: 153 MB DateTime array: 76 MB Int array: 38 MB String references: 8 MB

Next, we look at the memory usage from running the program in the second path in the Main entry point, which tests TestArray2. The memory usage here is much higher. The values and references are not allocated in contiguous memory.
Info: Memory usage is 267 MB total. This is 229 MB for all class object data and 38 MB for all class references in the array.

By using separate arrays, which contain all their values and references to objects in continuous storage, the program saves over 100 MB. If you look at Task Manager when running these Release executables, the difference is greater.
And: I am not certain why this occurs, but it also proves the massive memory efficiency gain.
Task manager measurement Program with three arrays: 40612 KB [smaller] Program with one array: 276936 KB

OOP. I consider object-oriented programming's greatest strength to be its separation of concerns. The class type itself is not the key to OOP, but rather the barricading of outside accesses to discrete units of data and behavior.
Therefore: If you change a class to use three arrays instead of a class array, you do not violate the principles of OOP.

Factory design pattern. If your program uses an object such as Employee that is stored in an array, you can store the data in separate arrays and return a new object of type Employee that is constructed lazily.
But: This is only useful when only one Employee object will be needed by the caller. And it could lead to more problems.
Factory Pattern

Summary. You can greatly decrease the memory usage of your C# programs by using arrays of value types instead of separate class instances. This allows more efficient data storage by the CLR.

© 2007-2020 Sam Allen. Every person is special and unique. Send bug reports to info@dotnetperls.com.