Утечка памяти в TList

У меня проблема с утечкой памяти при использовании TList. Я пытаюсь заполнить цикл Tlist через заполненный список и использовать данные. Приведенный ниже код — это только код для заполнения списка без его использования.

private
  { Private Form Variable declarations }
  GlblCancel : Boolean;
  MyPrintLst : TList;

PrintRecord = record
  PrintString1,
  PrintString2,
  PrintString3,
  PrintString4,
  PrintString5,
  PrintString6 : string;
  PrintFloat1,
  PrintFloat2,
  PrintFloat3  : Double;
end;
PrintPointer = ^PrintRecord;

Procedure TMyForm.Create;
begin
  MyPrintLst := TList.Create;
end

Procedure TMyForm.FreeTList(Var List : Tlist; Size : Integer);
Var I, Count : Integer;
begin
  Count := list.Count - 1;
  For I := Count DownTo 0 Do
     FreeMem(List[I], Size);
  List.Clear;
  List.Free;
end;

Procedure TMyForm.FormClose;
begin
  FreeTList(MyPrintLst,SizeOf(PrintRecord));
end

procedure AddToPrintList(PrintList : TList;
                         Const MyStrings : Array of String;
                         Const MyDoubles : Array of Double);
var
PrintPtr : PrintPointer;
begin
New(PrintPtr);
IF High(MyStrings) >= 0 Then
   PrintPtr^.printString1 := MyStrings[0];
 Begin
   IF High(MyStrings) >= 1 Then
    Begin
      PrintPtr^.printString2 := MyStrings[1];
      IF High(MyStrings) >= 2 Then
       Begin
         PrintPtr^.printString3 := MyStrings[2];
         IF High(MyStrings) >= 3 Then
          Begin
            PrintPtr^.printString4 := MyStrings[3];
            IF High(MyStrings) >= 4 Then
               PrintPtr^.printString5 := MyStrings[4];
             Begin
               IF High(MyStrings) >= 5 Then
                  PrintPtr^.printString6 := MyStrings[5];
             End; {>=5}
          End; {>=4}
       End; {>=3}
    End; {>=2}
 End; {>=1}
IF High(MyDoubles) >= 0 Then
 Begin
   PrintPtr^.PrintFloat1 := MyDoubles[0];
   IF High(MyDoubles) >= 1 Then
    Begin
      PrintPtr^.PrintFloat2 := MyDoubles[1];
      IF High(MyDoubles) >= 2 Then
         PrintPtr^.PrintFloat3 := MyDoubles[2];
    End;
 End;
PrintList.add(PrintPtr);
end;

Procedure TMyForm.Button1.Click;
Var EstReading : LongInt;
begin
EstReading := 0;
ClearTList(MyPrintLst,Sizeof(PrintRecord));
MyQuery.First;
While Not(MyQuery.EOF) Do
 begin
   EstReading := EstReading + 1;
   AddToPrintList(MyPrintLst, [MyQuery.FieldByName('Field1').AsString,
                               MyQuery.FieldByName('Field2').AsString, 
                               MyQuery.FieldByName('Field3').AsString, 
                               MyQuery.FieldByName*'Field4').AsString, 
                               MyQuery.FieldByName('Field5').AsString, 
                               MyQuery.FieldByName('Field6').AsString],
                               [EstReading]);
   MyQuery.Next;
 end;
end

person SCAJason    schedule 10.11.2015    source источник
comment
Пожалуйста, добавьте сообщение об утечке и весь код, который приведет к утечке.   -  person Sir Rufo    schedule 11.11.2015


Ответы (1)


Вы неправильно распоряжаетесь динамически размещенными записями. Ваша запись является управляемой, поскольку она содержит управляемые типы, в данном случае строки.

Управляемые типы при динамическом выделении таким образом должны выделяться с помощью New и освобождаться с помощью Dispose. Ваша ошибка в том, что вы используете FreeMem, а не Dispose. Последний будет удалять управляемые типы в записи, а первый — нет. Отсюда и ваша утечка.

Похоже, что у ClearTList такой же дефект.

Вы храните указатели в экземпляре TList, а этот тип хранит нетипизированные указатели. Когда вы удаляете каждый элемент, вы должны привести элемент обратно к соответствующему типу указателя, чтобы среда выполнения знала, как удалять поля в записи. Таким образом, ваш вызов Dispose будет выглядеть так:

Dispose(PrintPointer(List[I]));

Кроме того, передача размера элемента при вызове FreeMem довольно бессмысленна.

Обобщить:

  • Для неуправляемых типов используйте GetMem/FreeMem или New/Dispose.
  • Для управляемых типов используйте New/Dispose.
  • Всегда правильно сочетайте эти функции. Всегда FreeMem с GetMem, всегда Dispose с New.
person David Heffernan    schedule 10.11.2015
comment
Вызывать Clear бессмысленно, учитывая, что вы уничтожаете объект. Передавать список как var бессмысленно, поскольку вы не изменяете переменную. В любом случае, я думаю, что ответил на вопрос, который вы задали, нет? - person David Heffernan; 16.11.2015
comment
Я изменил freemem в процедурах freetlist и cleartlist на Dispose, но все еще получаю утечку памяти. - person SCAJason; 16.11.2015
comment
Я ответил на вопрос, который вы задали. Скорее всего у вас есть другие дефекты. Несправедливо ожидать, что мы будем отлаживать остальную часть вашего кода, которую мы не видим. Если бы вы предоставили минимально воспроизводимый пример, мы могли бы исправить там весь код. Но ты этого не сделал. Кстати, очень странно писать Count := List.Count-1. Используйте Count, чтобы удерживать количество, и не меньше, чем количество. Для этого нет необходимости создавать локальную переменную. Убери это. - person David Heffernan; 16.11.2015
comment
Как я могу опубликовать полный код? - person SCAJason; 16.11.2015
comment
Это очень поздно. Этому вопросу недельная давность. Я ответил на вопрос, который вы задали. Если у вас есть новый вопрос, сделайте его новым вопросом. Но я не сомневаюсь, что это ответ на вопрос, который вы задали. - person David Heffernan; 16.11.2015