Утечка памяти происходит во вложенном анонимном методе - PullRequest
15 голосов
/ 08 июня 2011

В 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

Ответы [ 3 ]

14 голосов
/ 08 июня 2011

Это связано с тем, как работают анонимные методы. Анонимные методы реализованы как потомки TInterfacedObject, и если у вас более одного в одной подпрограмме, они заканчиваются как два метода одного и того же объекта. Он использует интерфейсы для подсчета ссылок, поэтому вы не в конечном итоге утечка предметов. Однако, , если анонимный метод ссылается на себя, это приводит к сбросу счетчика ссылок и возникновению утечки памяти. То, что вы видите здесь, вызвано сочетанием этих двух вещей.

13 голосов
/ 08 июня 2011

Это ошибка в компиляторе (насколько я знаю). Я открыл 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;
3 голосов
/ 01 августа 2013

Я знаю, что я опоздал на это обсуждение на 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, что позволяет освободить интерфейсный объект.

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

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

...