Хотел добавить свой собственный ответ, потому что в противном случае хороший ответ от OwenP имеет две важные ошибки при использовании System.Runtime.MemoryFailPoint .
Первую ошибку очень просто исправить: сигнатура конструктора public MemoryFailPoint(int sizeInMegabytes)
, поэтому аргумент AverageFrameSize
должен быть в мегабайтах, а не в байтах.Также обратите внимание на следующее о размере:
MemoryFailPoint работает с гранулярностью 16 МБ.Любые значения, меньшие 16 МБ, обрабатываются как 16 МБ, а другие значения обрабатываются как следующий по величине кратный 16 МБ.
Вторая ошибка заключается в том, что экземпляр MemoryFailPoint
должен поддерживаться допосле выделения памяти, которую вы хотите использовать, , а затем утилизируйте!
Это может быть немного сложнее исправить и может потребовать внесения изменений в дизайн в зависимости от того, как выглядит реальный код OPнапример.
Причина, по которой вы должны располагать таким образом, заключается в том, что класс MemoryFailPoint
ведет запись всего процесса, зарезервированного для памяти, сделанную его конструктором.Это сделано, чтобы гарантировать, что если два потока выполнят проверку памяти примерно в одно и то же время, они оба не будут успешными, если не будет достаточно памяти для удовлетворения требований обоих потоков.(В противном случае класс MemoryFailPoint
будет бесполезен в многопоточных приложениях!)
Память, "зарезервированная" конструктором, не резервируется при вызове Dispose()
.Таким образом, поток должен располагать MemoryFailPoint
-инстанцией как можно скорее после , ему выделена необходимая память , но не ранее .(Часть «как можно скорее» предпочтительна, но не критична. Задержка утилизации может привести к ненужным сбоям других проверок памяти, но, по крайней мере, вы ошибаетесь на консервативной стороне.)
Выше указано требованиетребует изменения дизайна кода.Либо метод, проверяющий память, должен также выполнить выделение, либо он должен передать экземпляр MemoryFailPoint
вызывающей стороне, что заставляет вызывающих лиц распоряжаться им в нужное время.(Последнее, что делает пример кода на MSDN.)
Использование первого подхода (и фиксированный размер буфера) может выглядеть примерно так:
const int FrameSizeInMegabytes = 10; // 10MB (perhaps more is needed?)
const int FrameSizeInBytes = FrameSizeInMegabytes << 20;
// shifting by 20 is the same as multiplying with 1024 * 1024.
bool TryCreateImageBuffer(int numberOfImages, out byte[,] imageBuffer)
{
// check that it is theoretically possible to allocate the array.
if (numberOfImages < 0 || numberOfImages > 0x7FFFFFC7)
throw new ArgumentOutOfRangeException("numberOfImages",
"Outside allowed range: 0 <= numberOfImages <= 0x7FFFFFC7");
// check that we have enough memory to allocate the array.
MemoryFailPoint memoryReservation = null;
try
{
memoryReservation =
new MemoryFailPoint(FrameSizeInMegabytes * numberOfImages);
}
catch (InsufficientMemoryException ex)
{
imageBuffer = null;
return false;
}
// if you get here, there's likely to be enough memory
// available to create the buffer. Normally we can't be
// 100% sure because another thread might allocate memory
// without first reserving it with MemoryFailPoint in
// which case you have a race condition for the allocate.
// Because of this the allocation should be done as soon
// as possible - the longer we wait the higher the risk.
imageBuffer = new byte[numberOfImages, FrameSizeInBytes];
//Now that we have allocated the memory we can go ahead and call dispose
memoryReservation.Dispose();
return true;
}
0x7FFFFFC7
- это максимальный индексатор, допустимый в любом измерении для массивов однобайтовых типов; его можно найти на странице MSDN о массивах .
Второй подход (где вызывающий объект отвечает за экземпляр MemoryFailPoint
) может выглядеть примерно так:
const int AverageFrameSizeInMegabytes = 10; // 10MB
/// <summary>
/// Tries to create a MemoryFailPoint instance for enough megabytes to
/// hold as many images as specified by <paramref name="numberOfImages"/>.
/// </summary>
/// <returns>
/// A MemoryFailPoint instance if the requested amount of memory was
/// available (at the time of this call), otherwise null.
/// </returns>
MemoryFailPoint GetMemoryFailPointFor(int numberOfImages)
{
MemoryFailPoint memoryReservation = null;
try
{
memoryReservation =
new MemoryFailPoint(AverageFrameSizeInMegabytes * numberOfImages);
}
catch (InsufficientMemoryException ex)
{
return null;
}
return memoryReservation;
}
Это выглядит намного проще (и более гибко), но теперь решать самому вызывающемуобрабатывать экземпляр MemoryFailPoint
и утилизировать его в нужный момент времени.(Добавлена некоторая обязательная документация, так как я не придумал хорошее и описательное имя для метода.)
Важное замечание: Что означает «зарезервировано» в этом контексте
Память не «зарезервирована»msgstr "в том смысле, что гарантированно будет доступно (для вызывающего потока).Это означает только то, что когда поток использует MemoryFailPoint
для проверки памяти, предполагая, что он успешен, он добавляет свой объем памяти к общему процессу (статическому) «зарезервированному» количеству, которое отслеживает класс MemoryFailPoint
.Это резервирование приведет к тому, что любой другой вызов MemoryFailPoint
(например, из других потоков) будет воспринимать общий объем свободной памяти как фактический объем минус текущий «статический» «зарезервированный» объем всего процесса.(При удалении MemoryFailPoint
экземпляров они вычитают свою сумму из зарезервированной суммы.).Однако сама система распределения памяти не знает и не заботится об этом так называемом «резервировании», что является одной из причин того, что MemoryFailPoint
не имеет строгих гарантий.
Обратите внимание, что память «зарезервирована»просто отслеживается как сумма.Поскольку это не фактическое резервирование определенного сегмента памяти, это еще больше ослабляет гарантии, что иллюстрируется следующим разочарованным комментарием, найденным в справочном источнике:
// Note that multiple threads can still ---- on our free chunk of address space, which can't be easily solved.
Нетрудно догадаться, что такое цензурированное слово.
Вот интересная статья о , как преодолеть ограничение в 2 ГБ для массивов .
Также, если вам нужно выделить очень большие структуры данных, вам нужно знать о <gcAllowVeryLargeObjects>
, которые вы можете установить в своем app-config.
Ничего не стоит, что это на самом деле не имеет ничего общего с физической памятью исключительно, как того хотел ОП. На самом деле, одна из вещей, которые MemoryFailPoint
попытается сделать до того, как она сдастся и сообщит об ошибке, - это увеличить размер файла подкачки. Но он сделает очень приличную работу, чтобы избежать получения OutOfMemoryException
при правильном использовании, что по крайней мере вдвое меньше, чем хотел ОП.
Если вы действительно хотите принудительно перенести данные в физическую память, то, насколько я знаю, вы должны использовать нативную AllocateUserPhysicalPages , которая не самая простая вещь в мире. с множеством вещей, которые могут пойти не так, требует соответствующих разрешений и почти наверняка излишним. ОС не очень-то нравится, когда ей говорят, как управлять памятью, поэтому ей нелегко ...