jsondelphidelphi-xe5

TJSONObject Memory Leak in complex json Object


I'm trying to serve a REST API service locally in a small business environment using idHTTPserver with Delphi Xe5. I pre process the file before sending it to the client, something is going wrong at this point. The memory is not released after the process its completed.
The JSON Object created is sent correctly to the client after all (AngularJS App)

What am I doing wrong?

When I receive a HTTP Client request I do this..

Procedure TMain.IdHTTPServer1CommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
Var
  // Here Var Types

  Json,LFilename:String;
  ROOTCOMANDAS,TORDEN,DATA,MSG : TJSONObject;
  Dlnew,d : TJSONArray
  files:tfilestream;

Begin

LFilename := ARequestInfo.Document;

if AnsiSameText(LFilename, '/resto/orders/jsondata') then
begin
  files := TFileStream.Create('htd' + LFilename, fmOpenRead + fmShareDenyWrite);
  Json := ReadStringFromStream(files);
  files.Free;
  ROOTCOMANDAS := TJSONOBJECT.ParseJSONValue(TEncoding.ASCII.GetBytes(Json), 0) as TJSONOBJECT;
  try
    Data := ROOTCOMANDAS.Get('data').JSONValue as TJSONOBJECT;
    d := Data.Get('d').JSONValue as TJSONArray;
    dlnew := TJSONArray.Create;

    for LValue in d do
      if (LValue as TJSONOBJECT).GetValue('ss').Value = '0' then
        dlnew.AddElement(LValue);

    TORDEN := TJSONOBJECT.Create;

    Msg := TJSONOBJECT.Create;
    Msg.AddPair(TJSONPair.Create('t', TJSONString.Create('m5000_325')));
    Msg.AddPair(TJSONPair.Create('tipo', TJSONNumber.Create(5)));

    TORDEN.AddPair(TJSONPair.Create('msg', Msg));

    Msg := TJSONOBJECT.Create;

    Msg.AddPair(TJSONPair.Create('et', TJSONString.Create(ETAGL)));
    Msg.AddPair(TJSONPair.Create('d', dlnew));

    TORDEN.AddPair(TJSONPair.Create('data', Msg));
    TORDEN.AddPair(TJSONPair.Create('ok', TJSONTrue.Create));
    TORDEN.AddPair(TJSONPair.Create('md', TJSONNumber.Create(iFD)));
    TORDEN.AddPair(TJSONPair.Create('time', TJSONString.Create(UTC)));

    Json := TORDEN.ToString;

    AResponseInfo.CacheControl := 'no-cache';
    AResponseInfo.CustomHeaders.Values['Access-Control-Allow-Headers'] := 'Content-Type';
    AResponseInfo.CustomHeaders.Values['Access-Control-Allow-methods'] := 'GET,POST,OPTIONS';
    AResponseInfo.CustomHeaders.Values['Access-Control-Allow-origin'] := '*';
    AResponseInfo.CharSet := 'utf-8';
    AResponseInfo.Pragma := 'Public';
    AResponseInfo.Server := 'Drone';
    AResponseInfo.ContentText := Json;
  finally
    ROOTCOMANDAS.Free;
  end;

  exit;
end;

Solution

  • You are never releasing TORDEN variable, hence the memory leak, and error you get when you do try to release it is caused by following lines:

    for LValue in d do
      if (LValue as TJSONOBJECT).GetValue('ss').Value = '0' then
        dlnew.AddElement(LValue);
    

    LValue is owned by d and will be released when d is released, and you are adding LValue to dlnew that will also want to release it. You have ownership issue here because two objects want to own and release same contained object.

    Try following changes to solve the issue:

    for LValue in d do
      if (LValue as TJSONOBJECT).GetValue('ss').Value = '0' then
        begin
          dlnew.AddElement(LValue);
          // here you are saying that you don't want to release object when ROOTCOMANDAS is released
          LValue.Owned := false;
        end;
    // release ROOTCOMANDAS and d along with it
    ROOTCOMANDAS.Free;
    // Set back owned property to true so you don't leak objects
    for LVaue in dlnew do
      LValue.Owned := true;
    ...
        Json := TORDEN.ToString;
        TORDEN.Free;
    
    ... remove superfluous ROOTCOMANDAS.Free; in finally part