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.
Related
When building the application (code below) a hint is displayed:
H2077 Value assigned to objParam never used
How do I resolve this hint? Is it even applicable in my case?
function TESPGenerateParamList.RandomizationTimeConfiguration(SRandomizationTimeNode: string; eConfigType: string): Boolean;
var
objParam: Param;
sFirstNode : string;
nStartPos,nEndPos : word;
begin
try
try
objParam := ParamSchedulerRandomizationTime.Create;
if eConfigType = 'SETPARAM' then
begin
ParamSchedulerRandomizationTime(objParam).FrameType := Set_Param;
//TIMEOUT Node
sFirstNode := '';
if SearchNode(rsMinutes,SRandomizationTimeNode,sFirstNode,nStartPos,nEndPos,false) then
begin
ParamSchedulerRandomizationTime(objParam).SetParam(0, strtoint(trim(sFirstNode)));
end;
end
else if eConfigType = 'GETPARAM' then
begin
ParamSchedulerRandomizationTime(objParam).FrameType := Get_Param;
ParamSchedulerRandomizationTime(objParam).GetParam(0);
end;
slConfigurationList.AddObject(objParam.ClassName, objParam);
result := true;
except
on E: Exception do
begin
LogErrorMessage('uTESPGenerateParamList-->RandomizationTimeConfiguration' + E.Message);
result := false;
raise;
end;
end;
finally
objParam := nil; //(for here it give hint)
end;
end;
The compiler is absolutely correct. You don't refer to the variable after that assignment. The next thing that happens in all cases is that the function terminates.
To resolve the hint, delete the assignment statement entirely. Then you can remove the surrounding try-finally block, too, since nothing happens in the finally section.
But that's assuming the assignment statement was the proper way to dispose of the referenced object in the first place. It's probably not, if Param is a class type rather than an interface. In that case, keep the try-finally block, but replace the assignment with Param.Free, just like you've surely seen in dozens of other Delphi examples. Then, move the initial objParam assignment up two lines so it occurs before you enter the first try section.
Suppose I have the following routine:
function ReadFile(f : TFilename) : Boolean;
var
fs : TFileStream;
begin
Result := False;
try
fs := TFileStream.Create(f, ...);
try
// read file ...
Result := True;
finally
FreeAndNil(fs);
end;
except
// handle exceptions ...
end;
end;
What are the implications of having the except and finally transposed? I have seen plenty of posts with them both ways around, but I haven't seen a clear explanation of which is appropriate in which cases (I still think it is curious that in the above construct, the finally block executes after the except block!).
I have also seen posts that suggest that mixing try..except and try..finally blocks is not a good idea. How can you avoid it in situations where a routine throws an exception as part of normal operation - such as in some of the Indy routines?
There is no single correct way to write this. The two variants do different things. You may prefer one version in one scenario, the other in a different scenario.
Version 1, finally inner-most
function ReadFile(f : TFilename) : Boolean;
var
fs : TFileStream;
begin
Result := False;
try
fs := TFileStream.Create(f, ...);
try
// read file ...
Result := True;
finally
FreeAndNil(fs);
end;
except
// handle exceptions ...
end;
end;
Version 2, finally outer-most
function ReadFile(f : TFilename) : Boolean;
var
fs : TFileStream;
begin
Result := False;
fs := TFileStream.Create(f, ...);
try
try
// read file ...
Result := True;
except
// handle exceptions ...
end;
finally
FreeAndNil(fs);
end;
end;
The big difference is how the code behaves if TFileStream.Create raises an exception, a far from implausible eventuality. In version 1, the exception will be caught and handled inside ReadFile. In version 2, the exception will be passed out of ReadFile and on up the chain of exception handlers.
Asides
You state:
I still think it is curious that in the above construct, the finally block executes after the except block!
That is not true for the code in your question, version 1 above. Perhaps you don't yet fully understand how finally and blocks operate.
A common mistake that is often observed, is a desire to catch and handle exceptions as soon as possible. That's the wrong strategy. The whole point about an exception is that it is not meant to happen and you usually don't know what to do when it does happen. Your goal is to handle exceptions as late as possible. For the vast majority of code you should simply not handle exceptions. Let them float upwards to a point in the code that is able to deal with the error.
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;
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.