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? 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.
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.
Related
After 2 days wracking my brain, I give up on fixing this problem on my own.
I'm currently working on a service that needs to check the config of certain other services to decide if it can proceed with the rest of the program.
So far so good, it is working, but the clean up of the memory seems to be faulty. GetMem() and FreeMem() seem not to really give back the memory, and New() and Dispose() seem to break the memory allocation internally.
function tServiceStoppStart.GetServiceConfigStartTtype(sService: String): DWORD;
var
schm, schs: SC_Handle;
config: LPQUERY_SERVICE_CONFIG;
pcbBytesNeeded: DWORD;
sucessful: boolean;
begin
try
try
begin
sucessful := false;
// open the service manager (defined in WinSvc)
schm := OpenSCManager(nil, nil, SC_MANAGER_ALL_ACCESS);
if (schm > 0) then
begin
// grab the service handle
schs := OpenService(schm, PChar(sService), SERVICE_ALL_ACCESS);
if (schs > 0) then
begin
// get the byte count for the serviceconfig query
//over the pcbBytesNeeded witchz is filled with the right amount after 1. call
QueryServiceConfig(schs, config, 0, pcbBytesNeeded);
// 1. GetMem(config, pcbBytesNeeded);
// 2. New(config); -> seems to corrupt the memory allocation
if QueryServiceConfig(schs, config, pcbBytesNeeded, pcbBytesNeeded) then
begin
Result := config.dwStartType;
sucessful := True;
end;
end;
end;
CloseServiceHandle(schs);
end;
except
on E: Exception do
begin
//
end;
end;
finally
begin;
if sucessful then
begin
// seems not to free the memory
// 1. freeMem(config,pcbBytesNeeded)
// 2. Dispose(config) -> seems to corrupt the memory allocation
end;
end;
end;
end;
{Set the new first free block}
mov TSmallBlockPoolHeader[edx].FirstFreeBlock, ecx
{Set the block header} // gets accesviolation with new and dispose
mov [eax - 4], edx
{Is the chunk now full?}
jz #RemoveSmallPool
by object create calls
I'm not that confident in my record handling in Delphi. Can someone point me in the right direction?
Looks like it got fixed thoug the closing of the manager proably got meomry leak from build up by manager opening spam thx J... for the cue for the close
I have a simple TCP file server program developed in Delphi RIO + Indy TCP Server. When 2 or more clients asks for the files, the CPU runs very high in 90s. This spooks off the server team and during this time, they have hard time login into the server on which the program is running.
Based on other threads on the subject, when I put IndySleep(x), it does bring the CPU down and the avg stays in 50-60s. I understand that putting IndySleep() may throttle a bit, but it works!
The files it serves are already compressed and vary in size from 1KB to <10MB.
Is there anything else I can do to improve overall CPU usage, without or with little IndySleep()?
Here is the code snippet:
procedure TMainForm.IdTCPSyncServerExecute(AContext: TIdContext);
begin
if (not AContext.Connection.IOHandler.InputBufferIsEmpty)
and (AContext.Connection.Connected) then
begin
SendFile(AContext, AContext.Connection.IOHandler.ReadLn);
//IndySleep(1000 div IdTCPSyncServer.Contexts.Count); // For high CPU
IndySleep(500); // For high CPU
end;
end;
procedure TMainForm.SendFile(AContext: TIdContext; AFileName: string);
var
lStream: TFileStream;
begin
lStream := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite);
if Assigned(lStream) then
begin
try
WriteRespHeader(AContext, 1, lStream.Size); //Custom fn() writes back to client file size and other useful info
AContext.Connection.IOHandler.LargeStream := False; // 32-bit
lStream.Position := 0;
AContext.Connection.IOHandler.Write(lStream, lStream.Size);
finally
lStream.Free;
end;
AddLogMsg(AContext.Binding.PeerIP + ' : Sent File: ' + AFileName); // Thread.Queue() based logging
end;
end;
You have the call to IndySleep() in the wrong place. If there is nothing available from the client to read yet, you are exiting OnExecute immediately and coming right back in, creating a tight loop. That is where your high CPU usage is likely occurring. Sleep only when there is nothing available yet, eg:
procedure TMainForm.IdTCPSyncServerExecute(AContext: TIdContext);
begin
if (not AContext.Connection.IOHandler.InputBufferIsEmpty)
and (AContext.Connection.Connected) then
begin
SendFile(AContext, AContext.Connection.IOHandler.ReadLn);
end else begin
//IndySleep(1000 div IdTCPSyncServer.Contexts.Count); // For high CPU
IndySleep(500); // For high CPU
// or, use AContext.Connection.IOHandler.Readable() instead...
// or, use AContext.Connection.IOHandler.CheckForDataOnSoure() instead...
end;
end;
Alternatively, I usually suggest this kind of manual check instead:
procedure TMainForm.IdTCPSyncServerExecute(AContext: TIdContext);
begin
if AContext.Connection.IOHandler.InputBufferIsEmpty then
begin
AContext.Connection.IOHandler.CheckForDataOnSource(500{1000 div IdTCPSyncServer.Contexts.Count}); // For high CPU
AContext.Connection.IOHandler.CheckForDisconnect;
if AContext.Connection.IOHandler.InputBufferIsEmpty then Exit;
end;
SendFile(AContext, AContext.Connection.IOHandler.ReadLn);
end;
But really, in this case, a better solution is to simply not manually check for the presence of client data at all. If there is nothing available to read yet, just let IOHandler.ReadLn() block until something actually arrives, eg:
procedure TMainForm.IdTCPSyncServerExecute(AContext: TIdContext);
begin
SendFile(AContext, AContext.Connection.IOHandler.ReadLn);
end;
I have the code below, but when I try to free the variable checkID, I get an access violation error, and if I don't destroy it I will have a memory leak problem.
function TdtmData.CheckID(AID: String): Boolean;
var
checkID : TJSONObject;
clientModule : TcmClientModule;
ok : Boolean;
begin
Result := False;
try
try
clientModule := TcmClientModule.Create(Self);
checkID := clientModule.smMethodsServerClient.CheckID(AID);
ok := checkID.GetValue<Boolean>('Register', False);
if not(ok) then
raise Exception.Create('ID ERROR.');
finally
clientModule.DisposeOf;
checkID.Free; // <-- The error is here (Access violation)
end;
Result := ok;
except
on e : Exception do
raise Exception.Create(e.Message);
end;
end;
The smMethodsServerClient.CheckID(AID) method was created automatically through the TDSRestConnection component.
function TsmMethodsServerClient.CheckID(AID: string; const ARequestFilter: string): TJSONObject;
begin
if FCheckIDCommand = nil then
begin
FCheckIDCommand := FConnection.CreateCommand;
FCheckIDCommand.RequestType := 'GET';
FCheckIDCommand.Text := 'TsmMethodsServer.CheckID';
FCheckIDCommand.Prepare(TsmMethodsServer_CheckID);
end;
FCheckIDCommand.Parameters[0].Value.SetWideString(AIDPDV);
FCheckIDCommand.Execute(ARequestFilter);
Result := TJSONObject(FCheckIDCommand.Parameters[1].Value.GetJSONValue(FInstanceOwner));
end;
I also used the Datasnap REST Client Module wizard to create my class TcmClientModule.
JSONValue used as a parameter of DataSnap does not need to be Free.
In addition, if you release the memory of the parameter object, an error may occur when you release the DataSnap DataModule or when you call the interface where the parameter was used for the second time.
Even if you create a new JSONValue parameter every time to use the DataSnap interface, there are no problems such as memory leaks.
Moreover, JSONValue objects received as a result of the DataSnap interface should not be freed further.
=================================================
clientModule.DisposeOf;
This frees memory for checkID. However, there is no setting for "checkID := nil". The conditional statement below will always be executed and an error occurs when executing.
if Assigned(checkId) then
checkID.Free;
Maybe when you are doing this thing
clientModule.DisposeOf;
checkID will be destroyed, because checkID is a part of clientModule due to this part of code
clientModule.smMethodsServerClient.CheckID(AID);
You can try clear checkID first and then clear clientModule.
Update: Another way to avoid error is checking checkId before destroying.
Maybe this way is suitable:
if Assigned(checkId) then
checkID.Free;
Maybe besides this check you need check object for null too.
is this piece of code safe from memory leaks?
s := TStringList.Create; // create first object
try
// Here line comes that seems to be dangerous
s := GetSomeSettings; // Overrides reference to first object by second one
finally
s.free; // Destroying only second object, leave first object to live somewhere in memory
end;
function GetSomeSettings : TStringList;
var
rawString : string;
settings : TStringList;
begin
// Singleton pattern implementation
// Trying to find already existing settings in class variable
settings := TSettingsClass.fSettings;
// If there is no already defined settings then get them
if not Assigned(settings) then
begin
GetSettingsInDB(rawString);
TSettingsClass.fSettings := ParseSettingsString(rawString);
settings := TSettingsClass.fSettings;
end;
Result := settings;
end;
I'm wondering s := GetSomeSettings; potentially harmful and ignoring first object, keeps it in the memory?
Yes, the StringList created on line 1 is leaked.
Essentialy, you are doing:
s := TStringList.Create;
s := AnotherStringList;
AnotherStringList.Free;
As for the GetSomeSettings routine:
Normally it is not wise or encouraged to return newly created instances as function results, because you transfer the responsibility for ownership and destruction to the calling code. Unless you have a mechanism/framework in place that takes care of it, which seems to be the case with your TSettingsClass, but there is not enough evidence for that in this little piece of code.
Nevertheless, the combination of both pieces of code display another problem: After s.Free, TSettingsClass.fSettings is destroyed but not nil. Thus the second time GetSomeSettings is called, it returns a dangling pointer.
1) you should not ask when you can check in two minutes!
program {$AppType Console};
uses Classes, SysUtils;
type TCheckedSL = class(TStringList)
public
procedure BeforeDestruction; override;
procedure AfterConstruction; override;
end;
procedure TCheckedSL.BeforeDestruction;
begin
inherited;
WriteLn('List ',IntToHex(Self,8), ' going to be safely destroyed.');
end;
procedure TCheckedSL.AfterConstruction;
begin
WriteLn('List ',IntToHex(Self,8), ' was created - check whether it is has matched destruction.');
inherited;
end;
procedure DoTest; var s: TStrings;
function GetSomeSettings: TStrings;
begin Result := TCheckedSL.Create end;
begin
Writeln('Entered DoTest procedure');
s := TCheckedSL.Create; // create first object
try
// Here line comes that seems to be dangerous
s := GetSomeSettings; // Overrides reference to first object by second one
finally
s.free; // Destroying only second object, leave first object
end;
Writeln('Leaving DoTest procedure');
end;
BEGIN
DoTest;
Writeln;
Writeln('Check output and press Enter when done');
ReadLn;
END.
2) Still that could be safe in few niche cases.
in FPC (http://FreePascal.org) S could be a "global property" of some unit, having a setter which would free old list.
in Delphi Classic S could be of some interface type, supported by the created object. Granted, standard TStringList lacks any interface, but some libraries ( for example http://jcl.sf.net ) do offer interface-based string lists, with richer API (iJclStringList type and related).
in Delphi/LLVM all objects were made reference-counted, like interfaces without GUID's. So that code would be safe there.
You can declare S as a record - a so-called Extended Record having re-defined class operator Implicit so that the typecast s{record} := TStringList.Create would free the previous instance before assigning a new one. That is dangerous though, as it is VERY fragile and easy to misuse, and destroy the list in some other place leaving a dangling pointer inside the S record.
Your object may be not that vanilla TStringList, but some subclass, overriding constructors or AfterConstruction to register itself in some list, that would be all-at-once in some place. Kind of Mark/Sweep heap management around large chunk of workload. VCL TComponent may be seen as following this pattern: form is owning its component and frees them when needed. And this is what you - in reduced form - are trying to do with TSettingsClass.fSettings containter (any reference is 1-sized container). However those frameworks do require a loopback: when the object is freed it should also remove itself from all the containers, referencing it.
.
procedure TCheckedSL.BeforeDestruction;
begin
if Self = TSettingsClass.fSettings then TSettingsClass.fSettings := nil;
inherited;
end;
class procedure TSettingsClass.SetFSettings(Value);
var fSet2: TObject;
begin
if fSettings <> nil then begin
fSet2 := fSettings;
f_fSettings := nil; // breaking the loop-chain
fSet2.Destroy;
end;
f_fSettings := Value;
end;
class destructor TSettingsClass.Destroy;
begin
fSettings := nil;
end;
However then - by the obvious need to keep design symmetric - the registration should also be done as a part of the class. Who is responsible for de-registration is usually the one responsible for registration as well, unless there are reasons to skew the design.
procedure TCheckedSL.AfterConstruction;
begin
inherited;
TSettingsClass.fSettings := Self;
end;
...
if not Assigned(settings) then
begin
GetSettingsInDB(rawString);
TCheckedSL.Create.Text := ParseSettingsString(rawString);
settings := TSettingsClass.fSettings;
Assert( Assigned(settings), 'wrong class used for DB settings' );
end;
Result := settings;
I just have a problem with Delphi that i'm about creating a function in my app and this function is like this
function Get_Foundation_infos(): TFields;
begin
with TMyQuery.Create(nil) do
begin
try
Connection := DataBaseForm.DataBaseForm1.DataBase;
SQL.Add('SELECT * FROM `foundation_infos` WHERE `Id`=1');
Execute;
Result := Fields;
except
on E: Exception do
Result := nil;
end;
end;
end;
the problem is I can't free the TMyquery from the out side of the function for that I have a problem of memory leaks and the application stop after 2 or 3 minutes later ....
but if I do free the TMyquery Object inside the function, then I can't get the function result from the outside of the function call.
You need to ensure that the TMyQuery object outlives the TFields object which it owns. But you must also make sure that you destroy that TMyQuery object to avoid the leak.
The simplest way is to return the TMyQuery object from the function and let the caller read the Fields property. When you are done, destroy the TMyQuery object. And naturally you'll use try/finally to ensure that exceptions don't lead to leaks.
function CreateFoundationQuery: TMyQuery;
begin
Result := TMyQuery.Create(nil);
try
Result.Connection := DataBaseForm.DataBaseForm1.DataBase;
Result.SQL.Add('SELECT * FROM `foundation_infos` WHERE `Id`=1');
Result.Execute;
except
Result.Free;
raise;
end;
end;
I just find another way maybe when I created an object without referenced it to a variable with a name that the memory manager can't fix the memory leak in this situation for that I created a Variable that have the same Type of function returning and in this case the memory manager can fix any memory leak with this named variable ....
this is the new code of the variable ...
function Get_Foundation_infos(): TMyQuery;
var
q: TMyQuery;
begin
q := TMyQuery.Create(nil);
with q do
begin
try
Connection := DataBaseForm.DataBaseForm1.DataBase;
SQL.Add('SELECT * FROM `foundation_infos` WHERE `Id`=1');
Execute;
Result := q;
except
on E: Exception do
Result := nil;
end;
end;
end;