views:

120

answers:

5

Hi, I'm developing as small diabetes program using Delphi 5 and ADO. I do a little query like this:

function GetLowestGlucoseLevel(StartDate:string;EndDate:string): Integer;
var
  Q:TADOQuery;
begin
   try
      Q:=TADOQuery.Create(Application); //Separate unit, owner set to App
      Q.Connection:=dtMod.ADOCon;
      Q.DisableControls;
      Q.Close;
      Q.SQL.Clear;
      Q.SQL.Add('SELECT Min(qGlucose.Glucose) AS MinOfGlucose from qGlucose');
      Q.Parameters[0].Value:=StartDate;
      Q.Parameters[1].Value:=EndDate;
      Q.Open;

      Result:=Q.FieldByName('MinOfGlucose').AsInteger;

      Q.Close;
    finally
      Q:=nil;
      Q.Free; 
    end; 
end;

The query runs OK and returns the result as expected. However, when I checked Windows Task Manager, memory usage keeps on rising rather than decreasing after the query.

How to fix this?

Thanks!

+9  A: 

You are leaking the TADOQuery by first setting it to nil, and then calling Free on the nil variable (which does nothing)

jasonpenny
Even when I delete the Q:=nil, the leak is still there. So I think it's not the cause of it.
AFF
Windows Task Manager does not show the actual memory usage, http://delphi.about.com/od/delphitips2007/qt/memory_usage.htm . The code in the question only has the leak I described. You should use FastMM and set `ReportMemoryLeaksOnShutdown` as described in the answer by Bharat to find any other leaks.
jasonpenny
@AFF Jason is right. Setting `Q` to `nil` before calling `Free` will not free the `TADOQuery` object. When you removed it and still witnessed a memory leak it means you had multiple leaks an only fixed one of them. (either that or you didn't recompile).
codeelegance
@Jason: You're right. My mistake :-)
AFF
@Codeelegance: there is FreeandNil in D5 :)
AFF
A: 

As others pointed out, the finally section should have the 2 statements reversed, like so:

finally
  Q.Free; 
  Q:=nil;  // <- not even necessary since this is a local var
end; 

Or you can call SysUtils.FreeAndNil(Q) (if that is available in Delphi 5, not sure).

Beside that, the TaskManager is an awful instrument to determine memory usage anyway. You might release the memory for Q, but that does not automatically mean the Delphi memory manager releases the memory to the OS.

Arjan de Haan
+2  A: 
  • Did you install Delphi 5 updates? The RTM ADO implementation is known to have issues.
  • Use FastMM4, it should work with Delphi 5 as well, and tell you more about where the leaks are.
ldsandon
I already installed the ADO Update 2, but the leak still there. I also installed FastMM4. The leak appears only when i run the query. I couldn't find other leaks which relate to dynamically created forms, etc.
AFF
Did you configured FastMM4 to report leaks? It will collect a call stack and many other informations to pinpoint the leak, but only if instructed to do so, look at the FastMM4.inc file for the $defines to enable to get detailed informations and how.
ldsandon
The call stack report only shows up if you use FullDebugMode
Mason Wheeler
I finally figured it out how to use FastMM, it didn't detect any leak from my app. Does this mean it is safe to assume that my app doesn't have leaks?
AFF
FastMM does an excellent job but may not catch *every* leak, especially those who doesn't use the Delphi MM. Also you should understand how Delphi and Windows allocate and deallocate memory, they won't do it for every bite, they will perform it in larger blocks.
ldsandon
@IdSandon: Thanks! :)
AFF
+1  A: 

Quote:

finally
  Q:=nil;
  Q.Free; 
end; 

You're kidding, right? First nil the variable, then free it? You're a genius! :-)

Use:

finally
  Q.Free; 
  Q:=nil;
end; 

Or don't even bother assigning nil to it, since Q is a local variable...


But rereading your code, I notice you use Application as owner. As a result, it will not really be a leak, since it will be freed when the application is freed. If you use a form, it would be freed when the owner form gets freed.
What you should try is to call this query about 100.000 times to check if it keeps reserving memory or if it's just increasing memory until a certain size has been reached. The latter is more likely, since the memory is reserved for future ADO calls.

Workshop Alex
Hahaha, yes, one stupid genius i am! :-D ... anyway, when i call the query again and again and again and again... , the memory use keeps on raising. I suspect something fishy about the ADO itself.
AFF
And it's actually not an error. :-) You're just delaying freeing the memory until the application is freed. The Delphi ADO components are known to have some minor memory leaks, though.
Workshop Alex
@Workshop Alex: It depends on how you define "memory leak." You say it's a variable that never gets freed, so this is fine. I think a better description is a variable that remains allocated after you're done using it.
Mason Wheeler
+1; for explaining `the Application is the owner, so all memory will be released when the application ends`. So during the application run, memory usage will increase for every call of this method. `Reversing the := nil and Free will release memory at the end of the method`.
Jeroen Pluimers
@Mason Wheeler, if a variable gets freed in the end, it's not a leak, just a waste of resources for the duration of the application. Normally, you would use a form as owner, especially when you create a TQuery component in design-time as a form control.
Workshop Alex
@Workshop Alex: See, there's where your argument starts not making any sense. All variables are part of the heap, afterall, and the heap gets reclaimed by Windows when the program ends, so technically, you could argue that nothing ever gets leaked, right? You have a definition that may be technically correct but is functionally useless. The only *useful* definition of a memory leak is continuing to hold on to memory after you no longer need it.
Mason Wheeler
@Mason Wheeler, Yes and no. Technically, a good program should have freed all used memory at the moment it terminates. That's just good practice, even though Windows will clean it up afterwards. If the owner had been a form, the component would live as long as the form. Now it's owned by the application, thus it lives as long as the application component. Memory that needs to be cleaned by Windows just means your application hasn't handled all it's data, which could mean data loss. (But in 99.99% of all cases, the data that's leaked was useful at termination.)
Workshop Alex
A: 

Aside inversing the lines as Arjan, jasonpenny and WorkShop Alex said, you can use Process Explorer to see the real consumption of memory (Private Bytes) of the process. Task Manager is not really suited to this task, as it only shows the working set of the process.

Fabricio Araujo