Утечка памяти происходит во вложенном анонимном методе
В Delphi XE следующий код вызовет утечку памяти:
procedure TForm1.Button1Click(Sender: TObject);
var P, B: TProc;
begin
B := procedure
begin
end;
P := procedure
begin
B;
end;
end;
Запустите код с помощью
ReportMemoryLeaksOnShutdown := True;
и приглашение диспетчера памяти:
21-28 bytes: TForm1.Button1Click$ActRec x 1
Ответы
Ответ 1
Это ошибка в компиляторе (насколько я знаю). Я открыл QC83259 в главном центре Embarcadero.
Вы можете обойти эту ошибку, создав анонимную процедуру в рутине. Следующий код не будет протекать.
procedure TForm1.Button1Click(Sender: TObject);
var P, B: TProc;
begin
B := GetMethod(); //Note, the "()" are necessary in this situation.
P := procedure
begin
B;
end;
end;
function TForm1.GetMethod: TProc;
begin
Result := procedure
begin
end;
end;
Ответ 2
Это связано с тем, как работают анонимные методы. Анонимные методы реализованы как потомки TInterfacedObject, и если у вас более одного в одной и той же подпрограмме, они заканчиваются как два метода одного и того же объекта. Он использует интерфейсы для подсчета ссылок, поэтому вы не заканчиваете утечку объектов. Тем не менее, если анонимный метод ссылается сам на себя, который заканчивается сбросом счетчика ссылок и вызывает утечку памяти. То, что вы видите здесь, вызвано сочетанием этих двух вещей.
Ответ 3
Я знаю, что я на 2 года опоздал на эту дискуссию, но недавно я столкнулся с этой утечкой памяти в нашем коде, и я не мог заставить Кена дать ответ на работу. Таким образом, с помощью моего коллеги мы пришли к другому ответу, чтобы продолжать использовать вложенные анонимные методы, но избегаем утечек памяти.
Ниже приведен пример найденного решения:
procedure TForm1.Button1Click(Sender: TObject);
var P, B: TProc;
begin
B := procedure
begin
end;
P := procedure
begin
B;
end;
B := nil;
end;
Я убежден, что из-за того, что локальные переменные связаны с целью продления их жизни, чтобы анонимный метод использовал его за пределами области, в которой он был создан, он создает копию базового сопряженного объект, чтобы переместить переменную из стека в кучу, и при этом он вызывает AddRef, который увеличивает счетчик ссылок. Установка переменной в nil после ее использования вызывает Release, который, в свою очередь, уменьшает опорный счетчик до 0, что позволяет освободить связанный объект.
После этого мы не видели утечек памяти, которые происходили раньше.
Является ли это ошибкой или нет, я не могу ответить на это, но мне интересно услышать мнения других. Мы рассматриваем это как способ позволить нам продолжать использовать анонимные методы во вложенной форме, подобной этой.