Search code examples
delphimemory-leakssap-gui

Delphi/SAP Functions OCX/Logon Control: Memory Leak


I connect to a SAP server via SAP Logon Control TLB (which eats about 25MB of mem at first start!!) and then query some data. Each call requires ~200 kB. Because I don't want to reconnect every time, I store the connection and pass it to the SAP Function object every time I need it (it seems the object is copied, because this proc also costs about 6MB).

After I'm done querying, I free the object but the memory usage is not going down. Why?

Therefore, if I let the program run for about 4 hours, my memory is full and the PC crashes.

The code (simplified):

connection.pas (creates the connection):

SAPLogonCtrl : TSAPLogonControl;

constructor TCon.Create(usr, pswd, sys, appserv, sysnum, clnt);
begin
    inherited Create;
    SAPLogonCtrl := TSAPLogonControl.Create(nil);
    with SAPLogonCtrl do begin
        User := usr;
        Password := pswd;
        ...
        Client := clnt;
    end;
    FConnection := SAPLogonCtrl.NewConnection;
    FConnection.Logon(0, true);   //<------------- this needs ~25MB
end;

main.pas:

...
procedure TMain.Query;
var
    theQuery : TSomeQuery;
begin
    theQuery := TSomeQuery.Create;
    theQuery.Data1 := 'something gets here';
    theQuery.Data2 := 'here too';
    theQuery.Call;    // <------------------------ this needs about ~100kB
    ...
    theQuery.Free;    // <------------------------ nothing happens here, no mem freed!
end;
...

someQuery.pas (creates the object and calls the query):

var
    mySAPFunction: TSapFunctions;
    mySAPQuery: Variant;

...
procedure Call;
begin
    mySAPFunction := TSAPFunctions.Create;
    mySAPFunction.Connection := FConnection;   // <---- connection is passed (copied? costs about 5MB) from connection.pas
    mySAPFunction.RemoveAll; // removes prevous added interfaces
    mySAPQuery := mySAPFunction.Add('interface here');
    mySAPQuery.Call;
    ...
    // return the result
end;

I hope this is understandable and that someone can help me because with this memory leak my program is practically unusable :(

Thanks in advance, Eike.


Solution

  • You can force to release a variant interface instance by setting it to nil:

    procedure Call;
    begin
        mySAPFunction := TSAPFunctions.Create;
        mySAPFunction.Connection := FConnection;   // <---- connection is passed (copied? costs about 5MB) from connection.pas
        mySAPFunction.RemoveAll; // removes prevous added interfaces
        mySAPQuery := mySAPFunction.Add('interface here');
        mySAPQuery.Call;
        mySAPQuery := null; // will release the memory
    end;
    

    In fact, I think mySAPQuery should be made local to your Call procedure: in this case, the mySapQuery := null statement will be made by the compiler.