title: How to avoid long-living strings to cause generation 2 garbage collection
body: <p>I have an application where I keep log strings in circular buffers. When a log gets full, for every new insert, old strings will be released for garbage collection and then they are in generation 2 memory. Thus, eventually a generation 2 GC will happen, which I would like to avoid.</p>
  
  <p>I tried to marshal the string into a struct. Surprisingly, I still get generation 2 GC:s. It seems the struct still keeps some reference to the string. Complete console app below. Any help appreciated. </p>
  
  <pre><code>using System;
  using System.Collections.Generic;
  using System.Diagnostics;
  using System.Linq;
  using System.Runtime.InteropServices;
  using System.Text;
  using System.Threading.Tasks;
  
  namespace ConsoleApplication
  {
      class Program
      {
  
          [StructLayout(LayoutKind.Sequential)]
          public struct FixedString
          {
              [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
              private string str;
  
              public FixedString(string str)
              {
                  this.str = str;
              }
          }
  
          [StructLayout(LayoutKind.Sequential)]
          public struct UTF8PackedString
          {
              private int length;
  
              [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
              private byte[] str;
  
              public UTF8PackedString(int length)
              {
                  this.length = length;
                  str = new byte[length];
              }
  
              public static implicit operator UTF8PackedString(string str)
              {
                  var obj = new UTF8PackedString(Encoding.UTF8.GetByteCount(str));
                  var bytes = Encoding.UTF8.GetBytes(str);
                  Array.Copy(bytes, obj.str, obj.length);
                  return obj;
              }
          }
  
          const int BufferSize = 1000000;
          const int LoopCount = 10000000;
  
          static void Main(string[] args)
          {
              Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}",
                  "Type".PadRight(20), "Time", "GC(0)", "GC(1)", "GC(2)");
              Console.WriteLine();
              for (int i = 0; i &lt; 5; i++)
              {
                  TestPerformance&lt;string&gt;(s =&gt; s);
                  TestPerformance&lt;FixedString&gt;(s =&gt; new FixedString(s));
                  TestPerformance&lt;UTF8PackedString&gt;(s =&gt; s);
                  Console.WriteLine();
              }
              Console.ReadKey();
          }
  
          private static void TestPerformance&lt;T&gt;(Func&lt;string, T&gt; func)
          {
              var buffer = new T[BufferSize];
              GC.Collect(2);
              Stopwatch stopWatch = new Stopwatch();
              var initialCollectionCounts = new int[] { GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2) };
              stopWatch.Reset();
              stopWatch.Start();
              for (int i = 0; i &lt; LoopCount; i++)
                  buffer[i % BufferSize] = func(i.ToString());
              stopWatch.Stop();
              Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}",
                  typeof(T).Name.PadRight(20),
                  stopWatch.ElapsedMilliseconds,
                  (GC.CollectionCount(0) - initialCollectionCounts[0]),
                  (GC.CollectionCount(1) - initialCollectionCounts[1]),
                  (GC.CollectionCount(2) - initialCollectionCounts[2])
              );
          }
      }
  }
  </code></pre>
  
  <p>Edit: Updated code with UnsafeFixedString that does the required work:</p>
  
  <pre><code>using System;
  using System.Collections.Generic;
  using System.Diagnostics;
  using System.Linq;
  using System.Runtime.InteropServices;
  using System.Text;
  using System.Threading.Tasks;
  
  namespace ConsoleApplication
  {
      class Program
      {
          public unsafe struct UnsafeFixedString
          {
              private int length;
  
              private fixed char str[256];
  
              public UnsafeFixedString(int length)
              {
                  this.length = length;
              }
  
              public static implicit operator UnsafeFixedString(string str)
              {
                  var obj = new UnsafeFixedString(str.Length);
                  for (int i = 0; i &lt; str.Length; i++)
                      obj.str[i] = str[i];                
                  return obj;
              }
          }
  
          const int BufferSize = 1000000;
          const int LoopCount = 10000000;
  
          static void Main(string[] args)
          {
              Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}",
                  "Type".PadRight(20), "Time", "GC(0)", "GC(1)", "GC(2)");
              Console.WriteLine();
              for (int i = 0; i &lt; 5; i++)
              {
                  TestPerformance(s =&gt; s);
                  TestPerformance&lt;UnsafeFixedString&gt;(s =&gt; s);
                  Console.WriteLine();
              }
              Console.ReadKey();
          }
  
          private static void TestPerformance&lt;T&gt;(Func&lt;string, T&gt; func)
          {
              var buffer = new T[BufferSize];
              GC.Collect(2);
              Stopwatch stopWatch = new Stopwatch();
              var initialCollectionCounts = new int[] { GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2) };
              stopWatch.Reset();
              stopWatch.Start();
              for (int i = 0; i &lt; LoopCount; i++)
                  buffer[i % BufferSize] = func(String.Format("{0}", i));
              stopWatch.Stop();
              Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}",
                  typeof(T).Name.PadRight(20),
                  stopWatch.ElapsedMilliseconds,
                  (GC.CollectionCount(0) - initialCollectionCounts[0]),
                  (GC.CollectionCount(1) - initialCollectionCounts[1]),
                  (GC.CollectionCount(2) - initialCollectionCounts[2])
              );
          }
      }
  }
  </code></pre>
  
  <p>Output on my computer is:</p>
  
  <pre><code>Type                    Time    GC(0)   GC(1)   GC(2)
  
  String                  5746    160     71      19
  UnsafeFixedString       5345    418     0       0
  </code></pre>