简体   繁体   中英

Delphi DataSnap function returning TStream and memory leak

I am using a basic DataSnap server. I am using XE8, windows 8.1

I have the next server function:

function TSrvServerMetodos.ImagePac(pront:integer): TStream;
var blob:TStream;
    strm:TMemoryStream;
begin
  with qrytemp do begin
        Params.Clear;
        SQL.Clear;
        SQL.Add('SELECT F.PICTURE FROM CLIENTES F WHERE F.PRONT=:pront');
        UnPrepare;
        Prepare;
        Params[0].Value:=pront;
        Open;
        if IsEmpty then result:=nil
        else begin
         try
              Result := CreateBlobStream(fieldbyname('pict'),bmRead);
              GetInvocationMetadata.ResponseContentType := 'image/jpeg';
         except
              Result:=nil
         end;
        end;
  end;
end;

My client app is an Android app. All works well and I can get the pícture .

My doubt is about memory leak on Datasnap server.

Since result is a TStream that I created, Datasnap Server will take care to free it or I will have problems with memory leak?

I did an experiment to look into this and the result surprised me.

Let the DataSnapServer Wizard (under File | New | Other | Delphi Projects | DataSnap Server) create a default server project and then make the following additions

type
  TMyStringStream = class(TStringStream)
  public
    constructor Create(Value : String);
    destructor Destroy; override;
  end;

  TServerMethods1 = class(TDSServerModule)
[...]

function TServerMethods1.GetStream(Value: String): TStream;
var
  SS : TMyStringStream;
begin
  SS := TMyStringStream.Create(Value);
  SS.Position := 0;
  Result := SS;
end;

constructor TMyStringStream.Create(Value: String);
begin
  inherited Create;
  WriteString(Value);
end;

destructor TMyStringStream.Destroy;
begin
  Clear;
  inherited;
end;

, compile, put a breakpoint on Clear in TMyStringStream.Destroy and run.

Then, create/run a minimal client which does something like this

procedure TDSClientForm.Button2Click(Sender: TObject);
var
  SS : TStringStream;
  S : TStream;
begin
  SqlServerMethod2.Params[0].AsString := 'ABC123';
  SqlServerMethod2.ExecuteMethod;
  SS := TStringStream.Create;
  try
    S := SqlServerMethod2.Params[1].AsStream; //, 6{SqlServerMethod2.Params[1].Size});
    S.Position := 0;
    SS.CopyFrom(S, S.Size);
    ShowMessage(SS.DataString);
  finally
    SS.Free;
  end;
end;

If you then click Button2 in the client, you'll find that the server stops on the breakpoint in TMyStringStream.Destroy . This is a result of FreeAndNil(FAllocatedReturn) executing in the block

  if not FServerSideCommand then
    try
      FreeAndNil(FAllocatedReturn)
    except
      Result := false
    end;

in function TDSMethodValues.ClearReferenceParameters: boolean in DataSnap.DSReflect.

So it looks like a Stream which is allocated in your server method AND is assigned to its Result will be freed (whether you like it or not), so that won't leak. But I think you should regard that as a narrow exception to the general rule that if you create a Delphi object via a local variable, you should dispose of it before the variable goes out of scope.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM