Как убедить менеджер памяти освободить неиспользуемую память
В недавнем сообщении ( Моя программа никогда не освобождает память. Почему?) Я показываю, что при использовании FastMM приложение не освобождает значительные объемы памяти обратно в систему. Недавно я создал программу искусственного тестирования, чтобы убедиться, что проблема не в памяти, а в том, что она появляется только с FastMM.
В этой программе я создаю и уничтожаю объект (такой же, как тот, что использовался в предыдущем посте) 500 раз.
Требования к памяти ("Частный рабочий набор"):
Без FastMM
Перед запуском цикла: 1,2 МБ
После запуска цикла: 2,1 МБ
С FastMM (агрессивный режим отладки)
Перед запуском цикла: 2,1 МБ
После запуска цикла: 25 МБ
С FastMM (режим выпуска)
Перед запуском цикла: 1,8 МБ
После запуска цикла: 3 МБ
Если я запускаю цикл несколько раз, требования к памяти не увеличиваются. Это означает, что неизданная память используется повторно, так что это не утечка памяти (утечка памяти увеличит объем памяти на несколько КБ / МБ при каждом запуске).
Мои вопросы:
Как я могу отключить это поведение в FastMM? Это вообще возможно? Я знаю, что если я выпущу программу без FastMM или с FastMM Release Mode, она "потратит" умеренное количество оперативной памяти. Но отключение этого поведения по требованию поможет мне (нам?) Выявить утечки памяти. На самом деле в моем первом посте (см. Ссылку) многие люди предположили, что у меня есть утечка. Беспорядок был создан, очевидно, только из-за этого поведения. Нет, очевидно, что утечки нет. Это просто диспетчер памяти, который отказывается освобождать большие объемы памяти.
Это когда-нибудь освободит дополнительную память? Когда? Что вызывает это? Может ли программист вызвать это? Например, когда я знаю, что выполнил задачу, интенсивно использующую ОЗУ, и пользователь может некоторое время не использовать программу (свести ее к минимуму), могу ли я сбросить ОЗУ обратно в систему? Что происходит, когда пользователь открывает несколько экземпляров моей программы? Не будут ли они конкурировать за оперативную память?
4 ответа
РЕШИТЬ
По предложению Барри Келли, память будет автоматически освобождена FastaMM. Чтобы подтвердить это, я написал вторую программу, которая выделяла много оперативной памяти. Как только в Windows кончилось ОЗУ, использование памяти моей программой вернулось к своему первоначальному значению.
Задача решена. Спасибо, Барри.
Вы не должны думать об этом, как об "потере" оперативной памяти, правда. Думайте об этом как о "кешировании" неиспользуемой оперативной памяти. Диспетчер памяти удерживает неиспользуемую память вместо того, чтобы по какой-то причине возвращать ее обратно в ОС, и фактически вы столкнулись с этой причиной в своем вопросе.
Вы сказали, что продолжаете повторять одни и те же операции в цикле. Когда вы делаете это, у него все еще остается старая доступная память, и он может назначить ее немедленно, вместо того, чтобы запрашивать у Windows новый кусок кучи. Это один из приемов, который помещает "Fast" в "FastMM", и если это не сработает, ваша программа будет работать намного медленнее.
Вам не нужно беспокоиться о фигуре режима отладки FastMM. Это только для отладки, и вы не собираетесь выпускать программу, скомпилированную с FullDebugMode. И разница между "без FastMM" и "с FastMM Release Mode" составляет около 1 МБ, что незначительно на современном оборудовании. При низкой стоимости всего в 1 МБ вы получаете значительное повышение производительности. Так что не беспокойся об этом.
Частично, что делает FastMM быстрым, заключается в том, что он выделяет большой блок памяти и выделяет из него меньшие по размеру части одинакового размера. Если какая-либо часть блока используется, ни одна из них не может быть возвращена обратно в ОС.
Вы можете использовать другой менеджер памяти. Один из подходов заключается в том, чтобы направить все VirtualAlloc
, Распределения будут округлены, чтобы занимать всю страницу за раз, поэтому ваша программа может пострадать, если у вас много небольших выделений, но когда вы звоните VirtualFree
Вы можете быть уверены, что память больше не принадлежит вашей программе.
Другой вариант - перенаправить все в кучу ОС. использование HeapAlloc
, Вы даже можете включить кучу с низкой фрагментацией для вашей программы (по умолчанию в Windows Vista), что заставит ОС использовать стратегию, аналогичную используемой FastMM, но позволит вам использовать некоторые средства отладки и анализа от Microsoft, чтобы отслеживать использование памяти вашей программы с течением времени. Остерегайтесь, однако, что после того, как вы позвоните HeapFree
некоторые метрики могут по-прежнему отображать память как принадлежащую вашей программе.
Кроме того, рабочий набор относится к памяти, которая в настоящее время находится в физической памяти. То, что вы наблюдали увеличение числа, не означает, что ваша программа распределила больше памяти. Это может просто означать, что ваша программа коснулась некоторой памяти, которая была ранее выделена, но еще не была помещена в ОЗУ. Во время цикла вы коснулись этой памяти, и ОС еще не решила перенести ее обратно на диск.
Я использую следующее как менеджер памяти. Я делаю это потому, что он работает намного лучше в условиях конкуренции за потоки, чем FastMM, что на самом деле довольно плохо. Я знаю, что масштабируемый менеджер, такой как Hoard, будет лучше, но это отлично работает для моих нужд.
unit msvcrtMM;
interface
implementation
type
size_t = Cardinal;
const
msvcrtDLL = 'msvcrt.dll';
function malloc(Size: size_t): Pointer; cdecl; external msvcrtDLL;
function realloc(P: Pointer; Size: size_t): Pointer; cdecl; external msvcrtDLL;
procedure free(P: Pointer); cdecl; external msvcrtDLL;
function GetMem(Size: Integer): Pointer;
begin
Result := malloc(size);
end;
function FreeMem(P: Pointer): Integer;
begin
free(P);
Result := 0;
end;
function ReallocMem(P: Pointer; Size: Integer): Pointer;
begin
Result := realloc(P, Size);
end;
function AllocMem(Size: Cardinal): Pointer;
begin
Result := GetMem(Size);
if Assigned(Result) then begin
FillChar(Result^, Size, 0);
end;
end;
function RegisterUnregisterExpectedMemoryLeak(P: Pointer): Boolean;
begin
Result := False;
end;
const
MemoryManager: TMemoryManagerEx = (
GetMem: GetMem;
FreeMem: FreeMem;
ReallocMem: ReallocMem;
AllocMem: AllocMem;
RegisterExpectedMemoryLeak: RegisterUnregisterExpectedMemoryLeak;
UnregisterExpectedMemoryLeak: RegisterUnregisterExpectedMemoryLeak
);
initialization
SetMemoryManager(MemoryManager);
end.
Это не ответ на ваш вопрос, но он слишком длинный, чтобы вписаться в комментарий, и вам может показаться интересным запускать ваше приложение на этом ММ. Я предполагаю, что он будет работать так же, как FastMM.