Как отправить / получить TObjectList с помощью DataSnap, решая проблему утечки памяти?
Прототип клиентского приложения и сервера DataSnap. Я хочу передать TObjectList с сервера клиенту.
Это работает, но все объекты, которые я передаю, остаются в памяти на сервере и клиенте.
Что я делаю неправильно?
Жизненный цикл = Сессия
Объект TPessoa реализует TObjectList другого класса (TConta):
TConta = class(TObject)
private
FBanco: string;
FConta: Integer;
FAgencia: Integer;
procedure SetAgencia(const Value: Integer);
procedure SetBanco(const Value: string);
procedure SetConta(const Value: Integer);
published
property Banco : string read FBanco write SetBanco;
property Agencia : Integer read FAgencia write SetAgencia;
property Conta : Integer read FConta write SetConta;
end;
TContasCollection = TObjectList<TConta>;
TPessoa = class(TObject)
private
FContas: TContasCollection;
FId: Integer;
FNome: string;
procedure SetContas(const Value: TContasCollection);
procedure SetId(const Value: Integer);
procedure SetNome(const Value: string);
published
property Id : Integer read FId write SetId;
property Nome : string read FNome write SetNome;
property Contas : TContasCollection read FContas write SetContas;
end;
Метод public в ServerMetodsUnit:
function getPessoa(id : Integer) : Tpessoa;
function TServerMethods1.getPessoa(id: Integer): Tpessoa;
begin
result := Tpessoa.create;
result.id := id;
result.nome := 'NoName';
result.contas := getContas;
end;
function TServerMethods1.getContas: TContasCollection;
var conta : TConta;
begin
conta := TConta.Create;
conta.Banco := 'CEF';
conta.Agencia := 1;
conta.Conta := 123;
Result := TContasCollection.Create();
Result.Add(conta);
end;
Клиент:
procedure TForm2.btn1Click(Sender: TObject);
var pessoa : Tpessoa;
begin
pessoa := ClientModule1.ServerMethods1Client.getPessoa(1);
mmo1.Lines.Add(pessoa.Nome);
mmo1.Lines.Add(pessoa.Contas[0].Banco);
end;
правильный результат, однако сообщение об утечке памяти отображается на сервере и клиенте (System.ReportMemoryLeaksOnShutdown:= true;):
Неожиданная утечка памяти Произошла неожиданная утечка памяти. Неожиданные небольшие утечки блоков:
1 - 12 байт: TMoveArrayManager x 1, неизвестный x 1 13 - 20 байт: TConta x 1, UnicodeString x 1 37 - 44 байта: TObjectList x 1
как решить эту утечку памяти, чтобы не повлиять на сервис?
1 ответ
Утечка памяти происходит из-за того, что вы не освобождаете создаваемые объекты.
Для запуска в клиентской части вы создаете pessoa
объект в btn1Click
как локальная переменная, но вы не освобождаете ее.
procedure TForm2.btn1Click(Sender: TObject);
var pessoa : Tpessoa;
begin
pessoa := ClientModule1.ServerMethods1Client.getPessoa(1);
mmo1.Lines.Add(pessoa.Nome);
mmo1.Lines.Add(pessoa.Contas[0].Banco);
pessoa.Free; // you are no longer using pessoa object after that point so release it
end;
TPessoa
класс имеет FContas
поле, которое мы не видим, как создается или выпускается, возможно, вы только что пропустили код, а может, его вообще нет. Тем не мение, FContas
также был выпущен в какой-то момент. Если TPessoa
класс является владельцем FContas
поле (и, похоже, так и должно быть), то вам придется добавить деструктор TPessoa
класс, где вы бы освободить FContas
коллекция.
FContas
является TObjectList
что по умолчанию имеет добавленные объекты, и они будут освобождены, когда FContas
выпущен.
Также возможна утечка FContas
объект в SetContas
метод, но не зная, как этот код выглядит, трудно сказать, утечка или нет.
TPessoa = class(TObject)
....
public
destructor Destroy; override;
end;
destructor TPessoa.Destroy;
begin
FContas.Free;
inherited;
end;
procedure SetContas(const Value: TContasCollection);
begin
FContas.Free; // release old FContas collection if there is one
FContas := Value; // reference FContas grabs ownership here
end;
По сути, каждый объект, который вы создаете, вы должны освободить после того, как он вам больше не нужен, если только не существует какого-либо другого экземпляра объекта, который захватит владение этим объектом и сделает это за вас, например TObjectList
делает.
Выше код предполагает, что FContas
Поле является владельцем коллекции, которая в нем хранится, и важно, чтобы вы не захватывали эту ссылку и не держали ее вне срока действия объекта-владельца (TPessoa
) пример.
Например, следующий код будет неправильным:
procedure TForm2.btn1Click(Sender: TObject);
var
pessoa : Tpessoa;
contas: TContasCollection;
begin
pessoa := ClientModule1.ServerMethods1Client.getPessoa(1);
contas := pessoa.Contas;
pessoa.Free; // <-- after that point contas points to released object
mmo1.Lines.Add(contas[0].Banco); // <-- dangling pointer use
end;
Существуют также экземпляры объектов с подсчетом ссылок, которые автоматически освобождаются после того, как последняя ссылка на них выходит из области видимости (обычно потомки TInterfacedObject
класс), но управление ими - совсем другая история. Вы не используете их здесь, и я упоминаю их только для полноты.