SCAJason
SCAJason

Reputation: 29

Memory Leak in TList

I am having a memory leak problem when using a TList. I am trying to populate the Tlist loop throught the populated list and use the data. The code below is only the code for populating the list not using it.

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

Upvotes: 1

Views: 635

Answers (1)

David Heffernan
David Heffernan

Reputation: 613461

You are not disposing of the dynamically allocated records correctly. You record is managed, since it contain managed types, strings in this case.

Managed types, when allocated dynamically like this, need to be allocated with New, and deallocated with Dispose. Your mistake is to use FreeMem and not Dispose. The latter will dispose of the managed types within the record, the former does not. Hence your leak.

It seems likely that ClearTList has the same defect.

You are storing the pointers in a TList instance, and this type stores untyped pointers. When you dispose of each item you must cast the item back to the appropriate pointer type so that the runtime knows how to dispose of the fields within the record. So your call to Dispose will look like this:

Dispose(PrintPointer(List[I]));

As an aside, passing the size of the element when calling FreeMem is rather pointless.

To summarise:

  • For unmanaged types use GetMem/FreeMem, or New/Dispose.
  • For managed types use New/Dispose.
  • Always pair these functions correctly. Always FreeMem with GetMem, always Dispose with New.

Upvotes: 12

Related Questions