28 January, 2012

Сюрприз Profiling API

На днях, при отладке профайлера, на приложении выделяющим и инициализирующим массив в 2Gb (максимальный размер объекта в CLR), во время очередного GC, был замечен странный кратковременный всплеск потребления памяти - выделялось дополнительно еще 2Gb (итого пиково было 4Gb). После расследования выяснилось, что на время вызова ICorProfilerCallback::ObjectReferences() CLR выделяет массив для передачи в профайлер всех исходящих рефернсов соответствующего объекта. Если референсы занимают эти самые 2Gb, то будут выделен массив в 2Gb. Проблема возникает только тогда, когда ICorProfilerCallback::ObjectReferences() возвращает всегда S_OK и производится полный обход графа для профайлера.

Собственно приложение (число 9 в коде подобрано для CLR 4.0 x64):

using System;

namespace AllocationTest
{
  internal class Program
  {
    private static void Main()
    {
      var tmp = new object[0x80000000U / (uint) IntPtr.Size - 9];

      for (int n = 0; n < tmp.Length; ++n)
        tmp[n] = tmp;

      Console.Write("Press any key...");
      Console.ReadKey();
      Console.WriteLine();

      GC.KeepAlive(tmp);
    }
  }
}
P.S. Если убрать в примере запонение массива, то эффект наблюдается не будет.
P.P.S.CLR v4.5 x64 может выделять массивы больше чем 2Gb! Смотри gcAllowVeryLargeObjects.