2010-10-27 1 views
1

Я развиваю как небольшую программу диабета, используя Delphi 5 и ADO. Я делаю небольшой запрос:Delphi: TAdoQuery Memory Leak?

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; 

Запрос выполняется ОК и возвращает результат, как ожидалось. Однако, когда я проверял Диспетчер задач Windows, использование памяти продолжает увеличиваться, а не уменьшаться после запроса.

Как это исправить?

Спасибо!

+0

Дополнительный комментарий на код: Объект Q создается внутри подпрограммы, таким образом, вызов DisableControls/Close/Clear совершенно не нужны, потому что запрос не имеет никаких связанных элементов управления, не является открытым, а свойство SQL - _blank_ при создании. Влияние на производительность почти ничего не делает, делая вызовы, но я избегаю такого рода кода по своей природе, главным образом потому, что он упрощает обычную процедуру. – jachguate

+0

Дополнительный 2: текст запроса не содержит параметров, но вы назначаете два из них в следующей строке ... без delphi под рукой прямо сейчас, я не могу проверить, но я думаю, что произойдет исключение , или, по крайней мере, неопределенное поведение. – jachguate

+0

@Jachguate: об отключенных управлениях это просто привычка моей, чтобы она всегда отключала элементы управления перед выполнением вызова sql. Спасибо за указание, хотя :) О параметрах, я на самом деле вызываю запрос в файле доступа MS с параметрами, поэтому без установки параметров в моем коде это создаст исключение. – AFF

ответ

13

Вы утечку TADOQuery сначала установить его в ноль, а затем вызвать Free на нулевой переменной (которая ничего не делает)

+0

Даже когда я удаляю Q: = nil, утечка все еще существует. Поэтому я думаю, что это не причина этого. – AFF

+4

Диспетчер задач Windows не показывает фактическое использование памяти, http://delphi.about.com/od/delphitips2007/qt/memory_usage.htm. Код в вопросе касается только той утечки, которую я описал. Вы должны использовать FastMM и установить 'ReportMemoryLeaksOnShutdown', как описано в ответе Bharat, чтобы найти другие утечки. – jasonpenny

+3

@AFF Джейсон прав. Установка 'Q' на' nil' перед вызовом 'Free' не освободит объект' TADOQuery'. Когда вы удалили его и все еще видели утечку памяти, это означает, что у вас было несколько утечек, и только один из них был зафиксирован. (либо это, либо вы не перекомпилировали). –

0

Как и другие указывали, наконец, раздел должен иметь в 2 заявления вспять, как и :

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

Или вы можете вызвать SysUtils.FreeAndNil (Q) (если таковой имеется в Delphi 5, не уверен).

Кроме того, TaskManager - это ужасный инструмент для определения использования памяти в любом случае. Вы можете освободить память для Q, но это не означает, что диспетчер памяти Delphi освобождает память для ОС.

4
  • Вы установили обновления Delphi 5? Реализация RTM ADO известна как .
  • Используйте FastMM4, он должен работать с Delphi 5, а также сообщить вам больше о том, где утечки.
+0

Я уже установил ADO Update 2, но утечка все еще там. Я также установил FastMM4. Утечка появляется только при выполнении запроса. Я не мог найти другие утечки, которые относятся к динамически созданным формам и т. Д. – AFF

+0

Вы настроили FastMM4 для сообщения об утечках? Он будет собирать стек вызовов и многие другие данные, чтобы точно определить утечку, но только если это указано, посмотрите файл FastMM4.inc для $ define, чтобы получить подробную информацию и как это сделать. – 2010-10-27 14:45:12

+0

Отчет о стеке вызовов отображается только в том случае, если вы используете FullDebugMode –

2

Цитата:

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

Вы шутите, да? Сначала nil переменная, затем освободите ее? Ты гений! :-)

Использование:

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

Или даже не потрудились назначая ноль к нему, так как Q является локальной переменной ...


Но перечитывая свой код, я замечаю, вы используете приложение как владелец. В результате на самом деле это не будет утечкой, поскольку она будет освобождена при освобождении приложения. Если вы используете форму, она будет освобождена, когда форма владельца будет освобождена.
Что вы должны попробовать, так это называть этот запрос примерно 100 000 раз, чтобы проверить, сохраняет ли он память для хранения или просто увеличивает память до тех пор, пока не будет достигнут определенный размер. Последнее более вероятно, так как память зарезервирована для будущих вызовов ADO.

+0

Хахаха, да, один глупый гений! : -D ... во всяком случае, когда я вызываю запрос снова и снова и снова и снова ..., использование памяти продолжает расти. Я подозреваю что-то подозрительное в самой ADO. – AFF

+1

И это на самом деле не ошибка. :-) Вы просто откладываете освобождение памяти, пока приложение не будет освобождено. Однако компоненты ADO Delphi имеют некоторые небольшие утечки памяти. –

+0

@Workshop Alex: Это зависит от того, как вы определяете «утечку памяти». Вы говорите, что это переменная, которая никогда не освобождается, поэтому все в порядке. Я думаю, что лучшее описание - это переменная, которая остается выделенной после того, как вы ее используете. –

0

Помимо инвертирования линии, как Arjan, jasonpenny и WorkShop Alex сказал, вы можете использовать Process Explorer, чтобы увидеть реальное потребление памяти (Private Bytes) процесса. Диспетчер задач на самом деле не подходит для этой задачи, так как он показывает только рабочий набор процесса.

3

Delphi путь:

function GetLowestGlucoseLevel(const StartDate:string; const EndDate:string): Integer; 
var 
    Q:TADOQuery; 

begin 

    Q:=TADOQuery.Create(nil); //(nil) because local use only. Placed before try\finally block 
           //because if it fails to .create then there would be no object to 
           //.Free 
    try 

     Q.Connection := dtMod.ADOCon; 

     //------can erase these------ 
     //Q.DisableControls; //No controls attached so unnecessary 
     //Q.Close;   //Q is local and was never opened so no need to close 
     //Q.SQL.Clear;  //Q is local and was never filled so no need to 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.Free; 

     //Q := nil   //not needed because Q's scope is local 

    end; 
end;