Delphi: агрегация объектов и утечки памяти с использованием атрибута [слабый]

Я хочу построить класс TParent содержащий несколько дочерних объектов с помощью агрегации. Некоторые из объектов независимы, в то время как некоторые могут также зависеть от других детей. Все дочерние объекты должны иметь ссылку на родителя. Я также хочу использовать интерфейсы, где это возможно.

Для этого я использую TInterfacedObject для TParent а также TAggregatedObject для детей. Поскольку и ребенок, и родитель знают друг о друге, я использую слабые ссылки, чтобы избежать зависимости от циркуляра. На самом деле это поведение уже определено в TAggregatedObject, Все отлично работает, когда я использую только независимые дочерние объекты (TIndependantChild).

Проблема возникает, когда дочерний объект зависит также от других дочерних элементов, см. Конструктор TDependantChild, Я храню ссылку на другой дочерний объект в переменной fChild, которая помечена [weak] attibute, представленный в Delphi 10 Berlin. FastMM4 сообщает об утечке памяти при завершении работы:

Также нарушение доступа приводит к System.TMonitor.Destroy повышается, но это происходит только тогда, когда FastMM4 используется, а ReportMemoryLeaksOnShutDown имеет значение True.

program Project1;

{$APPTYPE CONSOLE}

uses
  FastMM4,
  System.SysUtils;

type
  IParent = interface
  ['{B11AF925-C62A-4998-855B-268937EF30FB}']
  end;

  IChild = interface
  ['{15C19A4E-3FF2-4639-8957-F28F0F44F8B4}']
  end;

  TIndependantChild = class(TAggregatedObject, IChild)
  end;

  TDependantChild = class(TAggregatedObject, IChild)
  private
    [weak] fChild: IChild;
  public
    constructor Create(const Controller: IInterface; const AChild: IChild); reintroduce;
  end;

  TParent = class(TInterfacedObject, IParent)
  private
    fIndependantChild: TIndependantChild;
    fDependantChild: TDependantChild;
  public
    constructor Create;
    destructor Destroy; override;
  end;

{ TParent }

constructor TParent.Create;
begin
  fIndependantChild := TIndependantChild.Create(Self);
  fDependantChild := TDependantChild.Create(Self, fIndependantChild);
end;

destructor TParent.Destroy;
begin
  fDependantChild.Free;
  fIndependantChild.Free;
  inherited;
end;

{ TDependantChild }

constructor TDependantChild.Create(const Controller: IInterface; const AChild: IChild);
begin
  inherited Create(Controller);
  fChild := AChild;
end;

var
  Owner: IParent; 

begin
  ReportMemoryLeaksOnShutDown := True;
  Owner := TParent.Create;
  Owner := nil;
end.

Я обнаружил, что использование [небезопасных] вместо [слабых] решает проблему, но согласно справке delphi

Его ([небезопасно]) следует использовать только вне системного блока в очень редких ситуациях.

Поэтому я не убежден, что мне следует использовать [unsafe] здесь, особенно когда я не понимаю, что происходит.

Итак, каковы причины утечек памяти в этой ситуации и как их преодолеть?

1 ответ

Решение

Проблема утечки и сбоя при использовании внешнего диспетчера памяти FastMM4 связана с следующей проблемой, касающейся завершения внутреннего HashMap, используемого для отслеживания слабых ссылок.

[REGRESSION XE2 / 10.1 Berlin] Невозможно использовать сторонние менеджеры памяти

Из-за этой проблемы невозможно использовать сторонние менеджеры памяти для обнаружения утечек в Delphi 10.1 и более новых версиях, включая внешний FastMM4.

Вот почему у вас есть проблемы с [weak] атрибут, и у вас нет проблем с [unsafe],


Что касается вашего кода, вы можете безопасно использовать [unsafe] в приведенном выше сценарии. Пока в документации есть предупреждение об использовании [unsafe] атрибут, это предупреждение на самом деле не объясняет, почему [unsafe] не должен использоваться.

Короче говоря, вы можете использовать [unsafe] атрибут при жизни экземпляра объекта, на который ссылается [unsafe] ссылка длиннее, чем время жизни самой ссылки.

Другими словами, вы должны убедиться, что у вас нет доступа [unsafe] ссылка после того, как экземпляр объекта, на который он указывает, был освобожден, и это все.

[unsafe] ссылки не обнуляются, когда объект, на который они указывают, уничтожен, и использование такой ссылки после того, как объект исчезнет, ​​приведет к исключениям нарушения доступа.

Замена [weak] атрибут с [unsafe] это все, что вам нужно сделать, чтобы иметь правильно работающий код, как вы его представили.

  TDependantChild = class(TAggregatedObject, IChild)
  private
    [unsafe] fChild: IChild;
Другие вопросы по тегам