Reputation: 21
Prototype of a client application and DataSnap server. I want to transmit a TObjectList from the server to the client.
This is working , but all the objects that I convey remain in memory on the server and the client.
What am I doing wrong ?
Lifecycle = Session
The TPessoa object implements a TObjectList of another class (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;
Method public in 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;
Client:
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;
correct result, however the memory leak message is displayed in the Server and the Client (System.ReportMemoryLeaksOnShutdown := true;):
Unexpected Memory Leak An unexpected memory leak has occurred. The unexpected small block leaks are:
1 - 12 bytes: TMoveArrayManager x 1, Unknown x 1 13 - 20 bytes: TConta x 1, UnicodeString x 1 37 - 44 bytes: TObjectList x 1
how to solve this memory leak not to affect the service ?
Upvotes: 2
Views: 911
Reputation: 28519
Memory leak occurs because you are not releasing objects you are creating.
For start in client part you are creating pessoa
object in btn1Click
as local variable but you are not releasing it.
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
class has FContas
field that we don't see how is created or released, maybe you have just omitted the code, and maybe it is not there at all. Anyway, FContas
has also be released at some point. If TPessoa
class is owner of FContas
field (and it looks like it should be) then you would have to add destructor to TPessoa
class where you would free FContas
collection.
FContas
is TObjectList
that by default owns added objects and those would be released when FContas
is released.
There is also possible leak of FContas
object in SetContas
method, but without knowing how that code looks it is hard to tell is it leaking or not.
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;
Basically, every object you create you have to free after you no longer need it, unless there is some other object instance that will grab ownership of that object and do it for you, like TObjectList
does.
Above code assumes that FContas
field is owner of collection it holds, and it is important that you don't grab that reference and hold it beyond lifetime of it's owner object (TPessoa
) instance.
For example, following code would be wrong:
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;
There are also reference counted object instances that are automatically released after the last reference to them goes out of scope (usually descendants of TInterfacedObject
class), but managing those is completely different story. You are not using them here and I am mentioning them only for completeness.
Upvotes: 1