title: Why does struct alignment depend on whether a field type is primitive or user-defined?
body: <p>In <a href="http://nodatime.org" rel="noreferrer">Noda Time</a> v2, we're moving to nanosecond resolution. That means we can no longer use an 8-byte integer to represent the whole range of time we're interested in. That has prompted me to investigate the memory usage of the (many) structs of Noda Time, which has in turn led me to uncover a slight oddity in the CLR's alignment decision.</p>
  
  <p>Firstly, I realize that this <em>is</em> an implementation decision, and that the default behaviour could change at any time. I realize that I <em>can</em> modify it using <a href="http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.structlayoutattribute" rel="noreferrer"><code>[StructLayout]</code></a> and <a href="http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.fieldoffsetattribute" rel="noreferrer"><code>[FieldOffset]</code></a>, but I'd rather come up with a solution which didn't require that if possible.</p>
  
  <p>My core scenario is that I have a <code>struct</code> which contains a reference-type field and two other value-type fields, where those fields are simple wrappers for <code>int</code>. I had <em>hoped</em> that that would be represented as 16 bytes on the 64-bit CLR (8 for the reference and 4 for each of the others), but for some reason it's using 24 bytes. I'm measuring the space using arrays, by the way - I understand that the layout may be different in different situations, but this felt like a reasonable starting point.</p>
  
  <p>Here's a sample program demonstrating the issue:</p>
  
  <pre><code>using System;
  using System.Runtime.InteropServices;
  
  #pragma warning disable 0169
  
  struct Int32Wrapper
  {
      int x;
  }
  
  struct TwoInt32s
  {
      int x, y;
  }
  
  struct TwoInt32Wrappers
  {
      Int32Wrapper x, y;
  }
  
  struct RefAndTwoInt32s
  {
      string text;
      int x, y;
  }
  
  struct RefAndTwoInt32Wrappers
  {
      string text;
      Int32Wrapper x, y;
  }    
  
  class Test
  {
      static void Main()
      {
          Console.WriteLine("Environment: CLR {0} on {1} ({2})",
              Environment.Version,
              Environment.OSVersion,
              Environment.Is64BitProcess ? "64 bit" : "32 bit");
          ShowSize&lt;Int32Wrapper&gt;();
          ShowSize&lt;TwoInt32s&gt;();
          ShowSize&lt;TwoInt32Wrappers&gt;();
          ShowSize&lt;RefAndTwoInt32s&gt;();
          ShowSize&lt;RefAndTwoInt32Wrappers&gt;();
      }
  
      static void ShowSize&lt;T&gt;()
      {
          long before = GC.GetTotalMemory(true);
          T[] array = new T[100000];
          long after  = GC.GetTotalMemory(true);        
          Console.WriteLine("{0}: {1}", typeof(T),
                            (after - before) / array.Length);
      }
  }
  </code></pre>
  
  <p>And the compilation and output on my laptop:</p>
  
  <pre><code>c:\Users\Jon\Test&gt;csc /debug- /o+ ShowMemory.cs
  Microsoft (R) Visual C# Compiler version 12.0.30501.0
  for C# 5
  Copyright (C) Microsoft Corporation. All rights reserved.
  
  
  c:\Users\Jon\Test&gt;ShowMemory.exe
  Environment: CLR 4.0.30319.34014 on Microsoft Windows NT 6.2.9200.0 (64 bit)
  Int32Wrapper: 4
  TwoInt32s: 8
  TwoInt32Wrappers: 8
  RefAndTwoInt32s: 16
  RefAndTwoInt32Wrappers: 24
  </code></pre>
  
  <p>So:</p>
  
  <ul>
  <li>If you don't have a reference type field, the CLR is happy to pack <code>Int32Wrapper</code> fields together (<code>TwoInt32Wrappers</code> has a size of 8)</li>
  <li>Even with a reference type field, the CLR is still happy to pack <code>int</code> fields together (<code>RefAndTwoInt32s</code> has a size of 16)</li>
  <li>Combining the two, each <code>Int32Wrapper</code> field appears to be padded/aligned to 8 bytes. (<code>RefAndTwoInt32Wrappers</code> has a size of 24.)</li>
  <li>Running the same code in the debugger (but still a release build) shows a size of 12.</li>
  </ul>
  
  <p>A few other experiments have yielded similar results:</p>
  
  <ul>
  <li>Putting the reference type field after the value type fields doesn't help</li>
  <li>Using <code>object</code> instead of <code>string</code> doesn't help (I expect it's "any reference type")</li>
  <li>Using another struct as a "wrapper" around the reference doesn't help</li>
  <li>Using a generic struct as a wrapper around the reference doesn't help</li>
  <li>If I keep adding fields (in pairs for simplicity), <code>int</code> fields still count for 4 bytes, and <code>Int32Wrapper</code> fields count for 8 bytes</li>
  <li>Adding <code>[StructLayout(LayoutKind.Sequential, Pack = 4)]</code> to every struct in sight doesn't change the results</li>
  </ul>
  
  <p>Does anyone have any explanation for this (ideally with reference documentation) or a suggestion of how I can get hint to the CLR that I'd like the fields to be packed <em>without</em> specifying a constant field offset?</p>