Monday 30 April 2018

Potential fix to Delphi XE5 Datasnap memory leak issue

As we all know for a long time, Datasnap causes a lot of memory leak even for the simple App Server method call. I can reproduce the issue with Delphi XE5 by running simple test application multiple times. The test application only opens the connection, calls an App Server method (does nothing) and then close the connection and also close the application if succeed (otherwise system will hang due to lack of resources). With any Datasnap test application, to reproduce the issue you only need to execute 500 instances of test client as a batch; once repeat the batch 7 times, the App Server memory usage reaches around 4G (with the help of adding {$SetPEFlags IMAGE_FILE_LARGE_ADDRESS_AWARE} in the App Server project file).

After digging into the source codes of Datasnap for some time, I think I might have found the problem. In the procedure of TDSTCPServerTransport.DoOnDisconnect, some handlers and the associated DBX objects are not freed. You can find them in the function of TDSJSONProtocolHandlerFactory.CreateProtocolHandler.

I believe that the new lines between "//fix" in the following procedure fix the major memory leak. Another change is to add the following line to the App Server method as pointed out by a lot of people.

GetInvocationMetadata.CloseSession := True;

I've retested the same secnario as above, the App Server memory usage only reaches 41M.

procedure TDSTCPServerTransport.DoOnDisconnect(AContext: IIPContext);
var
  TempData: TObject;
  Event: TDSTCPDisconnectEventObject;
begin
  if Assigned(FTDSTCPDisconnectEvent) and (AContext <> nil) and (AContext.Connection <> nil) and
    (FTcpServer <> nil) and FTcpServer.Active then
  begin
    Event := TDSTCPDisconnectEventObject.Create(AContext.Connection.GetObject);
    try
      OnDisconnect(Event);
    except
    end;
  end;
  TempData := AContext.Data;
  //fix - start
  if TempData is TDBXJSonServerProtocolHandler then
  begin
    AContext.Data := nil;
    (TempData as TDBXJSonServerProtocolHandler).Free;
  end
  else
  //fix - end
  begin
    AContext.Data := nil;
    TempData.Free;
  end;
{$IFNDEF POSIX}
  CoUninitialize;
{$ENDIF}
end;

BTW, I've downloaded Delphi 10.2.3 trial and rebuilt the App Server, however it still has the memory leak issue.