Lazarus

Free Pascal => General => Topic started by: jamestien on October 28, 2021, 05:18:22 pm

Title: [SOLVED] Listview owner data caused mem leak
Post by: jamestien on October 28, 2021, 05:18:22 pm
Sample program to feed listview with typed record data, caused memory leak (records not being freed from heap).

The reason I chose to use own data, is because its very fast. adding 1000 items with several subitems with default Listview.Add item procedure is painful slow. 

I never had problem with Delphi before, or Delphi handled the record memory automatically?

Can someone enlighten me on how to properly free the manually allocated memory for the records?

Code: Pascal  [Select][+][-]
  1. unit FormODLaz;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ComCtrls, LCLIntf, LCLType;
  9.  
  10.  
  11. type
  12.   PItemRecord = ^TItemRecord;
  13.   TItemRecord = packed record
  14.     ID: integer;
  15.     Name: string;
  16.   end;
  17.  
  18.  
  19. type
  20.   { TForm1 }
  21.   TForm1 = class(TForm)
  22.     LV1: TListView;
  23.     procedure FormCreate(Sender: TObject);
  24.     procedure FormDestroy(Sender: TObject);
  25.     procedure LV1Data(Sender: TObject; Item: TListItem);
  26.   private
  27.  
  28.   public
  29.  
  30.   end;
  31.  
  32. var
  33.   Form1: TForm1;
  34.  
  35.   IDList: TList;
  36.  
  37. implementation
  38. {$R *.lfm}
  39.  
  40. {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
  41. function GetItemRecord(index: INTEGER): PItemRecord;
  42. begin
  43.   Result := PItemRecord(IDList[index]);
  44. end;
  45.  
  46. { TForm1 }
  47.  
  48. procedure TForm1.FormCreate(Sender: TObject);
  49. var
  50.   itm : PItemRecord;
  51.   i: Integer;
  52. begin
  53.   IDList := TList.Create;
  54.   LV1.Items.BeginUpdate;
  55.  
  56.   for i := 1 to 100 do
  57.     begin
  58.  
  59.       itm := New(PItemRecord);                //<======= memory leak complained here
  60.  
  61.       itm^.ID := i;
  62.       itm^.Name := 'Item '+ i.ToString;
  63.  
  64.       IDList.Add(itm);
  65.  
  66.     end;
  67.  
  68.   LV1.Items.Count := IDList.Count;
  69.   LV1.Items.EndUpdate;
  70.  
  71. end;
  72.  
  73. procedure TForm1.FormDestroy(Sender: TObject);
  74. var
  75.   i: Integer;
  76. begin
  77.   LV1.Clear;
  78.   IDList.Free;
  79.   //FreeAndNil(IDList);
  80. end;
  81.  
  82. procedure TForm1.LV1Data(Sender: TObject; Item: TListItem);
  83. begin
  84.   if (Item<>nil) then
  85.   with GetItemRecord(Item.Index)^ do
  86.     begin
  87.       Item.Caption := ID.ToString;
  88.       Item.SubItems.Add(Name);
  89.     end;
  90. end;
  91.  
  92.  
  93. end.
  94.  
Title: Re: [HELP] Listview owner data caused mem leak
Post by: wp on October 28, 2021, 05:32:21 pm
TList stores only pointers, it does not know how to release the memory occupied by the items. Therefore you must do this by yourself. Simply iterate through the TList elements in FormDestroy and dispose the pointers:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormDestroy(Sender: TObject);
  2. var
  3.   i: Integer;
  4.   itm: PItemRecord;
  5. begin
  6.   // LV1.Clear;  --- not necessary
  7.   for i := 0 to IDList.Count-1 do
  8.   begin
  9.     itm := PItemRecord(IDList[i]);
  10.     Dispose(itm);
  11.   end;
  12.   IDList.Free;
  13. end;

Be careful because you must do the same when you delete indidivual list items or "clear" the entire list somewhere else in your code. You can create a new class, e.g. TItemRecordList, in which you can implement all this book-keeping once for all, so that you can never forget disposing the items. Or you create the IDList as a generic list (TFPList).
Title: Re: [HELP] Listview owner data caused mem leak
Post by: devEric69 on October 28, 2021, 05:38:40 pm
AFAIK (mnemonics), only the TfpXxxObjectsxxx{List | Map | ...} have a property \ way which allows to say if it'll free automatically what it's storing...
Title: Re: [HELP] Listview owner data caused mem leak
Post by: jamestien on October 29, 2021, 12:33:25 am
Quote
Be careful because you must do the same when you delete indidivual list items or "clear" the entire list somewhere else in your code. You can create a new class, e.g. TItemRecordList, in which you can implement all this book-keeping once for all, so that you can never forget disposing the items. Or you create the IDList as a generic list (TFPList).

Thank you wp, devEric69 for the advice. i will try all the suggestions.
Title: Re: [HELP] Listview owner data caused mem leak
Post by: jamestien on October 29, 2021, 12:38:10 am
This clean up code solved the mem leak problem. thank you wp!

Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormDestroy(Sender: TObject);
  2. var
  3.   i: Integer;
  4.   itm: PItemRecord;
  5. begin
  6.   // LV1.Clear;  --- not necessary
  7.   for i := 0 to IDList.Count-1 do
  8.   begin
  9.     itm := PItemRecord(IDList[i]);
  10.     Dispose(itm);
  11.   end;
  12.   IDList.Free;
  13. end;
TinyPortal © 2005-2018