Arjan Mol
Arjan Mol

Reputation: 61

Delphi XE8 unknown memory leaks in simple DataSnap client and server app

I have created a simple DataSnap client/server application with the wizard in Delphi XE8 using the echostring and reversestring sample methods. When I put "ReportMemoryLeaksOnShutdown := True" in the Server dpr and call the echostring and/or reversestring methods from the client the result is good but when I close the server application (after closing the client) I always get 2 or more unknown memory leaks. Is this a known bug which I can't find on the internet or is there a solution?

Server code:

unit ServerMethodsUnit;

interface

uses System.SysUtils, System.Classes, System.Json,
Datasnap.DSServer, Datasnap.DSAuth, DataSnap.DSProviderDataModuleAdapter;

type
{$METHODINFO ON}
  TServerMethods = class(TDataModule)
  private
    { Private declarations }
  public
    { Public declarations }
    function EchoString(Value: string): string;
    function ReverseString(Value: string): string;
  end;
{$METHODINFO OFF}

implementation

{%CLASSGROUP 'FMX.Controls.TControl'}

{$R *.dfm}


uses System.StrUtils;

function TServerMethods.EchoString(Value: string): string;
begin
  Result := Value;
end;

function TServerMethods.ReverseString(Value: string): string;
begin
  Result := System.StrUtils.ReverseString(Value);
end;

end.

dfm

object ServerContainer: TServerContainer
  OldCreateOrder = False
  Height = 271
  Width = 415
  object DSServer1: TDSServer
    Left = 96
    Top = 11
  end
  object DSTCPServerTransport1: TDSTCPServerTransport
    Server = DSServer1
    Filters = <>
    Left = 96
    Top = 73
  end
  object DSServerClass1: TDSServerClass
    OnGetClass = DSServerClass1GetClass
    Server = DSServer1
    Left = 200
    Top = 11
  end
end

dfm project file

program DataSnap_Server;

uses
  FMX.Forms,
  Web.WebReq,
  IdHTTPWebBrokerBridge,
  ServerMainForm in 'ServerMainForm.pas' {Form2},
  ServerMethodsUnit in 'ServerMethodsUnit.pas' {ServerMethods: TDataModule},
  ServerContainerUnit in 'ServerContainerUnit.pas' {ServerContainer: TDataModule};

{$R *.res}

begin
  ReportMemoryLeaksOnShutdown := True;
  Application.Initialize;
  Application.CreateForm(TForm2, Form2);
  Application.CreateForm(TServerContainer, ServerContainer);
  Application.Run;
end.

client side code generated source

// 
// Created by the DataSnap proxy generator.
// 14-5-2015 22:45:56
// 

unit ClientClassesUnit;

interface

uses System.JSON, Data.DBXCommon, Data.DBXClient, Data.DBXDataSnap, Data.DBXJSON, Datasnap.DSProxy, System.Classes, System.SysUtils, Data.DB, Data.SqlExpr, Data.DBXDBReaders, Data.DBXCDSReaders, Data.DBXJSONReflect;

type
  TServerMethodsClient = class(TDSAdminClient)
  private
    FEchoStringCommand: TDBXCommand;
    FReverseStringCommand: TDBXCommand;
  public
    constructor Create(ADBXConnection: TDBXConnection); overload;
    constructor Create(ADBXConnection: TDBXConnection; AInstanceOwner: Boolean); overload;
    destructor Destroy; override;
    function EchoString(Value: string): string;
    function ReverseString(Value: string): string;
  end;

implementation

function TServerMethodsClient.EchoString(Value: string): string;
begin
  if FEchoStringCommand = nil then
  begin
    FEchoStringCommand := FDBXConnection.CreateCommand;
    FEchoStringCommand.CommandType := TDBXCommandTypes.DSServerMethod;
    FEchoStringCommand.Text := 'TServerMethods.EchoString';
    FEchoStringCommand.Prepare;
  end;
  FEchoStringCommand.Parameters[0].Value.SetWideString(Value);
  FEchoStringCommand.ExecuteUpdate;
  Result := FEchoStringCommand.Parameters[1].Value.GetWideString;
end;

function TServerMethodsClient.ReverseString(Value: string): string;
begin
  if FReverseStringCommand = nil then
  begin
    FReverseStringCommand := FDBXConnection.CreateCommand;
    FReverseStringCommand.CommandType := TDBXCommandTypes.DSServerMethod;
    FReverseStringCommand.Text := 'TServerMethods.ReverseString';
    FReverseStringCommand.Prepare;
  end;
  FReverseStringCommand.Parameters[0].Value.SetWideString(Value);
  FReverseStringCommand.ExecuteUpdate;
  Result := FReverseStringCommand.Parameters[1].Value.GetWideString;
end;


constructor TServerMethodsClient.Create(ADBXConnection: TDBXConnection);
begin
  inherited Create(ADBXConnection);
end;


constructor TServerMethodsClient.Create(ADBXConnection: TDBXConnection; AInstanceOwner: Boolean);
begin
  inherited Create(ADBXConnection, AInstanceOwner);
end;


destructor TServerMethodsClient.Destroy;
begin
  FEchoStringCommand.DisposeOf;
  FReverseStringCommand.DisposeOf;
  inherited;
end;

end.

Own source

unit ClientMainForm;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    Button2: TButton;
    Label1: TLabel;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses
  ClientModuleUnit;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Label1.Caption := ClientModule.ServerMethodsClient.EchoString(Edit1.Text);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  Label1.Caption := ClientModule.ServerMethodsClient.ReverseString(Edit1.Text);
end;

end.

Upvotes: 6

Views: 1925

Answers (2)

hhaumann
hhaumann

Reputation: 272

I guess it is a known bug for XE8 by now, I think it's pretty serious, at least serious enough for us NOT to use XE8 before Embarcadero has given us an answer on what's going on. We had a similar issue in XE2, as far as I remember it was on heavy callbacks.

This Eurekalog doesn't tell me much, it looks like deep inside datasnap, sorry I don't know how to make the log more readable.

EDIT: I reported this issue to Embarcadero and got this response today:

// Hi Henrik,

Part of the memory leaks are due to a bug in the System.Collections.Generics.pas, we are looking at releasing a fix this issue in very near future.

brgds

Roy. //

Thought you might wanna know :)

Upvotes: 0

Zam
Zam

Reputation: 2940

Memory leak looks like always exist, or, we doing something wrong.

What I checked:

I move all server app code into the one unit. I try server app without FMX - with VCL. I try to create TDSServer, TDSTCPServerTransport, TDSServerClass in runtime with parents Self and Nil. I try with TServerMethod class owner TPersistance and TComponent (Delphi help says to use it). I try with compiled server app as 32 bit and 64 bit application in Delphi XE7 Update 1 and in Delphi XE8.

EurekaLog 7.2.2 cannot catch details about memory leak also. For avoid catching Access Violation by EurekaLog need to use DSServer1.Stop before exit.

As we could see Access Violation when you using EurekaLog happens there Basically it's in System.TObject.InheritsFrom(???) System._IsClass($64AE4E0,TDSServerTransport) Datasnap.DSCommonServer.TDSCustomServer.StopTransports Datasnap.DSCommonServer.TDSCustomServer.Stop Datasnap.DSServer.TDSServer.Stop Datasnap.DSServer.TDSServer.Destroy System.TObject.Free System.Classes.TComponent.DestroyComponents System.Classes.TComponent.Destroy System.Classes.TDataModule.Destroy System.TObject.Free System.Classes.TComponent.DestroyComponents FMX.Forms.DoneApplication System.SysUtils.DoExitProc System._Halt0 :00408da8 TObject.InheritsFrom + $8

Server app:

unit ufmMain;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  Datasnap.DSServer, Datasnap.DSTCPServerTransport, Datasnap.DSAuth, DataSnap.DSProviderDataModuleAdapter, Datasnap.DSCommonServer,
  IPPeerServer;

type
{$METHODINFO ON}
  TServerMethods = class(TComponent)
  private
    { Private declarations }
  public
    { Public declarations }
    function EchoString(Value: string): string;
  end;
{$METHODINFO OFF}


  TfmMain = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    DSServer1: TDSServer;
    DSTCPServerTransport1: TDSTCPServerTransport;
    DSServerClass1: TDSServerClass;
    procedure DSServerClass1GetClass(DSServerClass: TDSServerClass; var PersistentClass: TPersistentClass);
  end;

var
  fmMain: TfmMain;

implementation

{$R *.dfm}

uses System.StrUtils;

function TServerMethods.EchoString(Value: string): string;
begin
  Result := Value;
end;

procedure TfmMain.DSServerClass1GetClass(DSServerClass: TDSServerClass; var PersistentClass: TPersistentClass);
begin
  PersistentClass := TServerMethods;
end;

procedure TfmMain.FormCreate(Sender: TObject);
begin
  DSServer1 := TDSServer.Create(nil);
  DSServer1.Name := 'DSServer1';
  DSServer1.AutoStart := False;

  DSTCPServerTransport1 := TDSTCPServerTransport.Create(nil);
  DSTCPServerTransport1.Server := DSServer1;

  DSServerClass1 := TDSServerClass.Create(nil);
  DSServerClass1.Server := DSServer1;
  DSServerClass1.OnGetClass := DSServerClass1GetClass;

  DSServer1.Start;
end;

procedure TfmMain.FormDestroy(Sender: TObject);
begin
  DSServer1.Stop;

  DSServerClass1.Free;
  DSTCPServerTransport1.Free;
  DSServer1.Free;
end;

end.

Upvotes: 0

Related Questions