Memory layout of .NET Arrays

This post is inspired by Karl Seguin‘s blog regarding Why Arrays Normally Start At Zero where he explained some fundamental concepts behind arrays and  pointer arithmetics. He used a simple yet powerful example from C language, although the concept itself could be applied to any other language. I decided to expand on same example by creating equivalent code using C# and see how this memory is managed by .NET Runtime.

Some of you may question the need for a .NET developer to really understand this behind the scenes memory management of runtime. It’s understandable that idea behind these managed frameworks is take “extra” burden (of memory management etc) off developers so that they can focus on solving their domain specific concepts. However I also believe that in order to effectively use these frameworks, its critical to understand the plumbing that has been put together to make these frameworks work. I think Charles Petzold made a good case of why we need to spent sometime to understand the inner workings of frameworks in his .NET book zero.  Quote from his book below.

As operating systems, programming languages, class libraries, and frameworks provide an ever increasingly higher level of abstraction, we programmers can sometimes lose sight of all the mechanisms going on beneath the surface. What looks like a simple addition in code can actually involve many layers of low-level activity.

Often times we are confronted with a problem where we need to have a decent knowledge of this low-level activity to effectively troubleshoot that problem. Anyways let’s go back to the code example from Karl’s blog. You can write the same code in C# as follows.

class Program
{
  static void Main(string[] args)
  {
      int[] array = { 0, 10, 50, 250, 750, 1225 };
      unsafe
      {
           fixed (int* ptr = &array[0])
           {
               int x = *(ptr + 2);
            }
       }
   }
}

I will use WinDbg/SOS to analyze the memory layout for this array and other variables in this code example. For this, I will run this program on a x86 machine in debug mode ( to avoid getting some compiler optimization)  and attach the debugger once a value is getting assigned to variable x. At this point, I will ClrStack command with -l  switch to include local variables in output also. Below is the output of this command.

As you can see from the output above, we have three local variables in this frame. Let’s start by looking at the variable x located at address 0x0012ed88. As we can see that it contains a hex value of 32 ( decimal 50). Since we already know that other two variables ptr and array are pointing to the integer array, values contained for these variables are just address in memory. We can look at raw memory at these address locations, however, before that, let’s understand how an array is represented in memory. The memory layout of an array could vary depending upon if it’s an array of value type or reference type. Since our array is of value type, let’s go over memory layout for this specific case. In .NET, an array is represented in memory by not only its contents but it also contains two extra fields. The first field points to Method Table of array type itself. Without going into too much details about Method table, its used to describe the meta data regarding the type. We will shortly look into what type of details are contained in this field. Next entry is the size of the array. The rest of the fields will contain the contents of array itself. Now let’s try to correlate this knowledge of array memory layout with what we have in the code. Our first variable int[] array will be pointing to the first field in array, that we just learned will be method table. The integer pointer ptr is pointing to first content of the array whereas integer variable x contains the values from the array content in ptr incremented by 2. Following picture depicts the memory layout of  array with our variables from the code.

Let’s run the dd command for our variables array and ptr  and see what is contained there. The two address we got from ClrStack command output are 0x01f8b890 and 0x01f8b888. Let’s first run the dd command for address 0x01f8b890. Since this is the pointer to first element from the array contents, you can see from the figure below the contents are just the hex decimal notation of array content values of  0,10,50, 250, 750 and 1225 respectively.

So the last local variable to look at is array itself. From the local variables list of ClrStack command, it has an address of 0x01f8b888.  Let’s dump this memory location using dd command. As discussed before, the first field from this output (5e3d2974) is the address of method table. Next field should be the size of array, which is 6 in this case. Rest of the items are just the array contents, which is exactly the same as what we have for the variable ptr.

Lastly let’s take a quick look at what is contained in the method table field. For this we will run the DumpMT command. Figure below shows the output of this command, confirming the type as integer array. DumpMT output also provides some other juicy details about the metadata for the type, that I will hopefully blog some other day.

Until next, happy debugging.

Advertisements
This entry was posted in .NET. Bookmark the permalink.

3 Responses to Memory layout of .NET Arrays

  1. Johnston says:

    Nice post with interesting details. Will be curious to see how it looks on a x64

  2. Thanks Johnston. Primary difference will be the pointer size between x86 and x64. Will try to post memory layout for that version too.

  3. Pingback: Memory Layout of .NET Arrays (x64) | Only way solving a problem is by Debugging it.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s