Home About

January 7th, 2009

Measuring memory usage of object creation in C# - 1

As your application grows it can be useful to get an idea of how much memory a particular data structure is using in memory. Measuring memory in a garbage collected environment is a somewhat of a moving target as the Garbage collector is able to move things around in the background.The .NET libraries offer two ways of measuring your applications memory. The total amount of memory allocated to the process (which includes code, unique libraries) and the amount of memory used that the Garbage collector is aware off (which is only your applications variables). This second set is of course a subset of the total amount of memory.

Measuring your process memory usage

The .NET API allows you to retrieve the total amount of memory allocated to a process through the System.Diagnostics.Process class. This variable is useful is you are interested in the total memory footprint of your process, but it is too coarse to measure how much memory a single object takes.

long Process_MemoryStart = 0;
Process MyProcess = System.Diagnostics.Process.GetCurrentProcess();
Process_MemoryStart = MyProcess.PrivateMemorySize64;

This will return the total amount of memory uniquely allocated to your process in bytes (not shared with other processes).This measure of memory includes everything from code, libraries, the execution environment, stack, variables etc. If you try to measure the creation of a new object in memory using this variable it is likely to show no change.  It is very likely that the difference shown is zero as some memory for new objects is pre-allocated.

Using the garbage collector to gain insights into memory allocation

If you are building a multi threaded application the following code won’t work as expected, other threads might be too busy creating new objects and destroying to gain a clear insight into how much memory an object uses.We make use of the fact that the Garbage collector can report how much allocated memory it is aware off. As the Garbage collector manages just the objects we create for our application — we can use it to measure an objects size.

By calling System.GC.GetTotalMemory() we can obtain a measure of how much memory the Garbage collector is managing. If we call this function again after we allocated our object, we can calculate the difference.

// Measure starting point memory use
GC_MemoryStart = System.GC.GetTotalMemory(true);

// Allocate a new byte array of 20000 elements (about 20000 bytes)
MyByteArray = new byte[20000];

// Obtain measurements after creating the new byte[]
GC_MemoryEnd = System.GC.GetTotalMemory(true);

// Ensure that the Array stays in memory and doesn't get optimized away
MyByteArray[1] = 20;

By calling System.GC.GetTotalMemory with “true” as a parameter we ensure that the Garbage collector makes at least an efford to clean up before reporting the amount of memory under its management. To avoid any compiler optimizations that would shift code around, we need to make sure that our newly created byte array is accessed again after we make the second GetTotalMemory() call.

The code listed at the end of this post reports something similar to the following when run:

Memory Use reported by GC: 20480
Memory Use reported by Process: 65536
Memory allocated by the Garbage collector: 135168 bytes
Memory allocated by the process: 2641920 bytes

As you can see, allocating a 20,000 element byte array takes up about 20Kb (with some additional overhead). Apparently the garbage collector decided to increase the applications total memory by 64Kb as the total process size increased by that much.You mileage may vary depending on your run. The final two figures show how much memory is under management by the Garbage collector for our simple application (about 135Kb) and the how much the Operating system needed to allocate it the whole application (2 Megabyte).

using System;
using System.Diagnostics;

namespace ProfileTest
{
	class MainClass
	{
		public static void Main(string[] args)
		{
	      long GC_MemoryStart = 0;
    	  long GC_MemoryEnd = 0;
		  long Process_MemoryStart = 0;
		  long Process_MemoryEnd = 0;
		  byte[] MyByteArray;
		  
		  Process MyProcess = System.Diagnostics.Process.GetCurrentProcess();

		  // Measure starting point memory use
      	  GC_MemoryStart = System.GC.GetTotalMemory(true);
		  Process_MemoryStart = MyProcess.PrivateMemorySize64;

		  // Allocate a new byte array of 20000 elements (about 20000 bytes) 
		  MyByteArray = new byte[20000];

		  // Obtain measurements after creating the new byte[]
		  GC_MemoryEnd = System.GC.GetTotalMemory(true);
		  Process_MemoryEnd = MyProcess.PrivateMemorySize64;
				
		  // Ensure that the Array stays in memory and doesn't get optimized away
		  MyByteArray[1] = 20;

		  // Show the difference after allocating the memory 
		  Console.WriteLine("Memory Use reported by GC: {0}" , GC_MemoryEnd - GC_MemoryStart );
		  Console.WriteLine("Memory Use reported by Process: {0}" , Process_MemoryEnd - Process_MemoryStart );

		  // Show how much memory was allocated by the process, and how much by the GC
		  Console.WriteLine("Memory allocated by the Garbage collector: {0} bytes", GC_MemoryEnd);
		  Console.WriteLine("Memory allocated by the process: {0} bytes", Process_MemoryEnd);
		}
	}
}
Be Sociable, Share!

Tags: , , , , ,

One Response to “Measuring memory usage of object creation in C#”

  1. Jared Says:

    Hello,
    I don’t think the PrivateMemory64 measures the application’s ram, it seems static. The performanceCounter class seems like a better indication.

    If you set the Class to “.NET CLR Memory” and “# Bytes Used” and Instance to your application name it gives RAM close to what you see in the TaskManager.
    Best,
    Jared


Most popular
Recent Comments
  • ARS: great plugin! I love it! but, it will be so nice if you can add attribute ‘title’ as one of...
  • Nelson: Saved me from doing it myself. Good article.
  • andy: i am currently playing taiwanese server wow in 奈辛瓦里(PVP) and i would like to realm transfer to somewhere there...
  • berties: any english speaking playing on a taiwanese server?
  • web application development: has C# search volume really so constant over the years? really surprising.