Примечание: 32-битное приложение, которое не планируется переводить на 64-битное.
Я работаю с очень потребляющим память приложением и в значительной степени оптимизировал все соответствующие пути в отношении выделения / отмены выделения памяти. (нет утечек памяти, утечек дескрипторов, никаких других утечек в самом приложении AFAIK и протестировано. Сторонние библиотеки, к которым я не могу прикоснуться, конечно, являются кандидатами, но маловероятными в моем сценарии)
Приложение часто выделяет большие одномерные и двумерные динамические массивы одиночных и упакованных записей до 4 одиночных записей. По большому счету я имею в виду 5000x5000 записей (одиночных, одиночных, одиночных, одиночных) - это нормально. Кроме того, одновременно работают 6 или 7 таких массивов. Это необходимо, поскольку на этих массивах выполняется много перекрестных вычислений, и их чтение с диска было бы настоящим убийцей производительности.
Уточнив это, я часто получаю ошибки памяти из-за этих больших динамических массивов, которые не исчезнут после их выпуска, независимо от того, установил ли я для них значение 0 или завершил их. Это, конечно, то, что FastMM делает для того, чтобы быть быстрым, я это хорошо знаю.
Я отслеживаю как выделенные блоки FastMM, так и потребляемую память (RAM + PF), используя:
function CurrentProcessMemory(AWaitForConsistentRead:boolean): Cardinal;
var
MemCounters: TProcessMemoryCounters;
LastRead:Cardinal;
maxCnt:integer;
begin
result := 0;// stupid D2010 compiler warning
maxCnt := 0;
repeat
Inc(maxCnt);
// this is a stabilization loop;
// in tight loops, the system doesn't get
// much chance to release allocated resources, which in turn will get falsely
// reported by this function as still being used, resulting in a false-positive
// memory leak report in the application.
// so we do a tight loop here, waiting, until the application reported memory
// gets stable.
LastRead := result;
MemCounters.cb := SizeOf(MemCounters);
if GetProcessMemoryInfo(GetCurrentProcess,
@MemCounters,
SizeOf(MemCounters)) then
Result := MemCounters.WorkingSetSize + MemCounters.PagefileUsage
else
RaiseLastOSError;
if AWaitForConsistentRead and (LastRead <> 0) and (abs(LastRead - result)>1024) then
begin
sleep(60);
application.processmessages;
end;
until (not AWaitForConsistentRead) or (abs(LastRead - result)<1024) or (maxCnt>1000);
// 60 seconds wait is a bit too much
// so if the system is that "unstable", let's just forget it.
end;
function CurrentFastMMMemory:Cardinal;
var mem:TMemoryManagerUsageSummary;
begin
GetMemoryManagerUsageSummary(mem);
result := mem.AllocatedBytes + mem.OverheadBytes;
end;
Я запускаю код на 64-битном компьютере, и мое максимальное потребление памяти до сбоев составляет около 3,3 - 3,4 ГБ. После этого я получаю сбои, связанные с памятью / ресурсами, в любом месте приложения. Мне потребовалось некоторое время, чтобы разобраться в использовании больших динамических массивов, которые были похоронены в какой-то сторонней библиотеке.
Способ, которым я справляюсь, заключается в том, что я заставил приложение возобновить себя с того места, где оно было остановлено, перезапустив себя и закрывшись с определенными параметрами. Это все прекрасно, если потребление памяти удовлетворительное и текущая операция завершена.
Большая проблема возникает, когда текущее использование памяти составляет 1 ГБ, а для обработки следующей операции требуется 2,5 ГБ памяти или более. Мой текущий код ограничился верхним значением 1,5 ГБ используемой памяти перед возобновлением, но в этой ситуации мне пришлось бы снизить предел до 1 ГБ, что в основном заставило бы приложение возобновлять само себя после каждой операции, и даже не это гарантировало что все будет хорошо.
Что, если для другой операции потребуется обработать больший набор данных, и для этого потребуется в общей сложности 4 ГБ или больше памяти?
Следует отметить, что я не говорю о фактических 4 ГБ в памяти, а о потребляемой памяти путем выделения огромных динамических массивов, которые ОС не получает обратно после отмены выделения и, следовательно, по-прежнему считает их потребленными, поэтому складывается.
Итак, моя следующая цель - заставить fastmm освободить всю (или хотя бы часть) памяти для ОС. Я специально нацелен здесь на огромные динамические массивы. Опять же, они находятся в сторонней библиотеке, поэтому перекодирование, которое на самом деле не входит в число лучших вариантов. Намного проще и быстрее повозиться с кодом fastmm и написать процедуру для освобождения памяти.
Я не могу переключиться с FastMM, так как в настоящее время все приложение и некоторые сторонние библиотеки сильно закодированы с использованием PushAllocationGroup, чтобы быстро находить и определять любые утечки памяти. Я знаю, что могу написать фиктивный модуль FastMM для решения ссылок на компиляцию, но я останусь без этого быстрого и надежного обнаружения утечек.
В заключение: есть ли способ заставить FastMM высвободить хотя бы некоторые из своих больших блоков в ОС? (ну, конечно, есть, собственно вопрос: кто-нибудь написал это, и если да, то поделитесь мнениями?)
Спасибо
позже редактировать:
Скоро я предложу небольшое тестовое приложение. Похоже, не так-то просто создать его макет.
result := 0
, тогдаLastRead := result
прочитает неинициализированную переменную. - person David Heffernan   schedule 19.12.2013RaiseLastOSError
. Конечно, нельзя ожидать, что компилятор узнает, что там внутри. Так что я бы не назвал компилятор тупым. У меня есть совсем другой способ заткнуть компилятор. Я бы создал перегрузкуRaiseLastOSError
, которая принимает нетипизированный параметрvar
, который он игнорирует, прежде чем перенаправить вызов на реальныйRaiseLastOSError
. ПройдитеResult
, и компилятор отключится. - person David Heffernan   schedule 19.12.2013