Исключение памяти в 1,2 ГБ

Я читал о пределе памяти

У меня есть приложение, которое работает с огромными изображениями, которые должны быть потоковыми. Как в обработке видео с одиночными кадрами. Приложение имеет около 40 плагинов, каждый из которых может содержать базу данных, обработку изображений и графический интерфейс WPF.

В приложении также есть 2 плагина, которые используют более старые DotNet Winforms.

Все работает хорошо, за исключением того, что приложение занимает около 1,2 ГБ в ОЗУ. Затем в необычных местах в плагинах, где выделена новая память, я получаю "исключение из памяти".

Я работаю над системой 64Bit, скомпилированной как 32Bit. Я больше не знаю, что делать и как искать любую ошибку.

Есть ли предел или я могу их поймать?

Ответ 1

Очень сложно написать 32-разрядную программу, которая потребляет все доступное пространство виртуальной памяти. Вы попадете в стену значительно ниже 2 гигабайт, из-за чего вы закончите с первого раза, это кусок виртуальной памяти, достаточно большой, чтобы соответствовать требуемому размеру. Вы можете подняться до предела 2 ГБ, сделав небольшие выделения, достаточно маленькие, чтобы вписаться в отверстия.

Эта стена поражает в начале программы, которая управляет растровыми изображениями. Они могут потреблять большой кусок виртуальной машины для хранения растровых пикселей, и это должно быть смежным распределением. Они хранятся в array, а не tree. Это неуправляемое выделение памяти, типичные профили памяти .NET, как правило, немного беспомощны, чтобы показать вам проблему.

Нет ничего разумного в том, что вы можете сделать для фрагментации адресного пространства, понятие о том, что потребление всей доступной виртуальной машины должно быть возможно, просто неверно. Вы можете получить больше пространства для передышки в 64-битной операционной системе, запустив editbin.exe в событии post post build и воспользуйтесь опцией командной строки /LARGEADDRESSAWARE. Это позволяет процессу использовать доступные 4 гигабайта виртуальной машины, вариант, характерный для 64-разрядной версии Windows, и возможный, поскольку Windows не нуждается в верхних 2 ГБ. И, конечно же, изменение целевой платформы в AnyCPU - это быстрый и простой способ получить копии виртуальной памяти.

Ответ 2

Приложение 32Bit, работающее в Windows (даже если ОС 64Bit) имеет 4Gb адресное пространство, но оно разделено на 2Gb Application/2Gb System (это можно изменить на 3/1 с помощью другого запускающего переключателя).

Весьма вероятно, что общая память, которую вы используете, на самом деле составляет 2 ГБ, а не 1,2 ГБ, как вы определяете эту цифру 1,2 ГБ, посмотрели приложение, используя обработчик процесса?

Если вы изменили приложение на ANYCPU или 64Bit, вы должны обнаружить, что это ограничение исчезает (хорошо перемещается в более крупное значение) на 64-битной ОС.

Ответ 3

Чтобы стать более детерминированным, вы должны написать некоторые интеграционные тесты, чтобы проверить, где находится ваша память. Теперь вы можете сделать это с помощью WMemoryProfiler. Я бы сначала загрузил изображения размером 1500x1500, очистил все, а затем отметьте все объекты, как известно. Затем я переместил большие изображения и проверил, какие новые объекты были выделены, и посмотрим, сколько из них есть и кто их владеет.

Вы говорите, что есть много внешних модулей. Возможно, вам стоит отказаться от некоторых из них из-за неразумного использования памяти и заменить их чем-то лучшим. Теперь вы можете проверить.

Если вы достигли предела, вы все равно можете выгружать некоторые изображения и загружать их по требованию, если вы и ваши плагины поддерживаете ленивые структуры, такие как IEnumerable<Image>, где вы, как поставщик, можете решить, когда загружать изображения и как долго их поддерживать это в кеше, пока вы не избавитесь от ссылки, чтобы освободить память.

[Test]
public void InstanceTracking()
{
   using (var dumper = new MemoryDumper())  // if you have problems use to see the debugger windows true,true))
   {
      TestWith1500x1500();
      dumper.MarkCurrentObjects();
      TestWith3000x3000();
      ILookup<Type, object> newObjects = dumper.GetNewObjects()
                                               .ToLookup( x => x.GetType() );

      // here we do find out which objects are holding most of the memory
      MemoryStatistics statOld = dumper.GetMemoryStatistics();
      foreach (var typeInfo in statOld.ManagedHeapStats
                                   .OrderByDescending(x => x.Value.Count))
      {
            Console.WriteLine("Type {0} has {1} instances of total size {2:N0} bytes", 
                           typeInfo.Key, 
                           typeInfo.Value.Count,
                           typeInfo.Value.TotalSize);
      }

      // then check with the info from above who is holding the most interesting new objects. 
      Console.WriteLine("New Strings:"); // just an example perhaps you should have a look at the images.
      foreach (var newStr in newObjects[typeof(string)] )
      {
          Console.WriteLine("Str: {0}", newStr);
      }
   }
}