Recent

Author Topic: [SOLVED] Listview owner data caused mem leak  (Read 1597 times)

jamestien

  • New Member
  • *
  • Posts: 35
[SOLVED] Listview owner data caused mem leak
« 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.  
« Last Edit: October 29, 2021, 12:38:45 am by jamestien »
Lazarus 2.0.12/2.2.0RC1 (Win10, Xubuntu, Pop!Os)

wp

  • Hero Member
  • *****
  • Posts: 9054
Re: [HELP] Listview owner data caused mem leak
« Reply #1 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).
« Last Edit: October 28, 2021, 05:34:19 pm by wp »
Mainly Lazarus trunk / fpc 3.2.0 / all 32-bit on Win-10, but many more...

devEric69

  • Hero Member
  • *****
  • Posts: 644
Re: [HELP] Listview owner data caused mem leak
« Reply #2 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...
« Last Edit: October 28, 2021, 05:54:38 pm by devEric69 »
use: Linux 64 bits (Ubuntu 20.04 LTS).
Lazarus version: 2.0.4 (svn revision: 62502M) compiled with fpc 3.0.4 - fpDebug \ Dwarf3.

jamestien

  • New Member
  • *
  • Posts: 35
Re: [HELP] Listview owner data caused mem leak
« Reply #3 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.
Lazarus 2.0.12/2.2.0RC1 (Win10, Xubuntu, Pop!Os)

jamestien

  • New Member
  • *
  • Posts: 35
Re: [HELP] Listview owner data caused mem leak
« Reply #4 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;
Lazarus 2.0.12/2.2.0RC1 (Win10, Xubuntu, Pop!Os)

 

TinyPortal © 2005-2018