Исключение памяти в 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);
}
}
}