Delphi: утечка памяти в TAdoQuery?

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

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, использование памяти продолжает расти, а не уменьшаться после запроса.

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

Спасибо!

6 ответов

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

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

Путь Дельфи:

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;

Цитата:

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

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

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

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

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


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

Как отмечали другие, в разделе finally должны быть изменены два оператора, например:

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

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

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

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

Другие вопросы по тегам