Почему мой класс занимает так много места в памяти?

У меня будут буквально десятки миллионов экземпляров некоторого класса MyClass и я хочу минимизировать его размер памяти. Вопрос об измерении того, сколько места занимает объект в памяти, обсуждался в узнайте размер объекта .net Я решил последовать совету Джона Скита, и вот мой код:--4-->

   // Edit: This line is "dangerous and foolish" :-) 
   // (However, commenting it does not change the result)
   // [StructLayout(LayoutKind.Sequential, Pack = 1)]
   public class MyClass       
   {
      public bool isit;
      public MyClass nextRight;
      public MyClass nextDown;
   }

   class Program
   {
      static void Main(string[] args)
      {
         var a1 = new MyClass(); //to prevent JIT code mangling the result (Skeet)
         var before = GC.GetTotalMemory(true);   
         MyClass[] arr = new MyClass[10000];
         for (int i = 0; i < 10000; i++)
            arr[i] = new MyClass(); 

         var after = GC.GetTotalMemory(true);

         var per = (after - before) / 10000.0;
         Console.WriteLine("Before: {0} After: {1} Per: {2}", before, after, per);
         Console.ReadLine();
      }
   }

Я запускаю программу на 64-битных окнах, выбираю "release", platform target: "any cpu" и выбираю "optimize code" (параметры имеют значение только если я явно платформ x86) результат, к сожалению, 48 байт на экземпляр.

мой расчет будет 8 байт на ссылку, плюс 1 байт для bool плюс некоторые ~ 8byte накладные расходы. Что происходит? Это заговор, чтобы держать цены на ОЗУ высокими и / или позволить не-Microsoft код раздуваться? Ну, хорошо, я думаю, мой настоящий вопрос: что я делаю неправильно, или как я могу минимизировать размер MyClass?

Edit: прошу прощения за небрежность в моем вопросе, я отредактировал пару имен идентификаторов. Мой конкретная и непосредственная задача заключалась в создании "2-dim linked-list" как разреженной реализации boolean matrice, где я могу легко получить перечисление заданных значений в данной строке/столбце. [Конечно, это означает, что я должен также хранить координаты x,y в классе, что делает мою идею еще менее осуществимой]

3 ответов


подойти к проблеме с другого конца. Вместо того, чтобы спрашивать себя: "как я могу сделать эту структуру данных меньше и все еще иметь десятки миллионов из них?"спросите себя", как я могу представить эти данные, используя совершенно другую структуру данных, которая намного компактнее?"

похоже, вы создаете двусвязный список модулей, который, как вы заметили, использует в тридцать-пятьдесят раз больше памяти, чем нужно. Есть ли какая-то причина, по которой вы не просто используете BitArray чтобы сохранить свой список bools?

обновление:

на самом деле я пытался реализовать разреженную булеву двумерную матрицу

Ну почему вы сразу не сказали об этом?

когда я хочу сделать разреженную Булеву двухмерную матрицу огромного размера, я строю неизменяемый постоянный логический quadtree С memoized фабрикой. Если массив разрежен или даже если он плотный, но самоподобный каким-то образом вы можете достичь огромный массаж. Квадратные массивы 264 x 264 булевы легко представимы, хотя, очевидно, как реальный массив, это было бы больше памяти, чем существует в мире.

Я играл с идеей сделать серию статей в блоге об этой технике; я, вероятно, сделаю это в конце марта.

Короче говоря, идея состоит в том, чтобы сделать абстрактный класс Quad, который имеет два подкласса: Одиночные и мульти. "Сингл" - Это дублет, как и синглтон, но с двумя точными примерами, называемыми истинным и ложным. Мульти-это квадроцикл, который имеет четыре суб-квадроцикла, называемые Северо-Восток, Юго-Восток, Юго-Запад и Северо-Запад.

каждый квадроцикл имеет целое число "уровень"; уровень одного равен нулю, а мульти уровня n требуется, чтобы все его потомки были квадроциклами уровня n-1.

Multi фабрика memoized; когда вы просите его сделать новое Multi с 4 детьми, оно консультируется с кэшем, чтобы узнать, сделал ли он это раньше. Если да, то он не строит новый; он раздает старый. Поскольку квадроциклы неизменяемы, вам не нужно беспокоиться о том, что кто-то меняет квадроцикл на вас после того, как он находится в кэше.

Теперь рассмотрим, сколько слов памяти (слово 4 или 8 байт в зависимости от архитектуры) потребляет "все false" Multi уровня N. Уровень 1 "All false" multi потребляет четыре слова для ссылок на своих детей, слово для подсчета уровня (если необходимо; вы не обязаны поддерживать уровень в мульти, хотя это помогает для отладки) и пару слов для блока синхронизации и так далее. Назовем это восемью словами. (Плюс память для ложного одиночного квадрата, который, как мы можем предположить, является постоянным двумя или тремя словами, и поэтому может быть проигнорирован.)

уровень 2" All false " multi потребляет те же восемь слов, но каждый из его четырех детей тот же уровень 1 multi. Поэтому общее потребление уровня 2 "все ложные" multi-это, скажем, 16 слов.

то же самое для уровня 3, 4,... и так далее. Общее потребление памяти для уровня 64 multi, который логически является 264 x 264 квадратный массив булевых только 64 x 16 слов памяти!

смысл? Надеюсь, этого наброска будет достаточно, чтобы вы начали. Если нет, посмотрите мой блог в конце марта.


8 (ссылка на объект) + 8 (ссылка на объект) + 1 (bool) + 16 (заголовок) + 8 (ссылка в самом массиве) = 41

даже если он смещен внутренне, каждый будет выровнен по куче. Итак, мы ищем по крайней мере 48bytes.

Я не могу за жизнь мне понять, почему вы хотели связанный список значений. Список из них займет 48 раз меньше места, и это до того, как вы получите оптимизацию хранения bool за бит, что сделает его в 384 раза меньше. И проще манипулировать.


Если эти сотни миллионов экземпляров класса в основном являются копиями класса с незначительными изменениями значений свойств класса, то ваша система является основным кандидатом на использование того, что называется Flyweight узор. Этот шаблон минимизирует использование памяти, используя одни и те же instanes снова и снова и просто изменяя свойства по мере необходимости...