Утечка памяти происходит во вложенном анонимном методе

В 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, что позволяет освободить связанный объект.

После этого мы не видели утечек памяти, которые происходили раньше.

Является ли это ошибкой или нет, я не могу ответить на это, но мне интересно услышать мнения других. Мы рассматриваем это как способ позволить нам продолжать использовать анонимные методы во вложенной форме, подобной этой.