[SOLVED] TStringList.free does not really release memory? lazarus / 34-Windows ---------------------------------------------------------------------- senglit · 2020-04-26 18:39 Hi all, I have a procedure which is to find some files' location in a folder. The structure of the folder is already known so that I used some TStringList to store the result of each step. There are more than 20,000 subfolders in the sRootDir. And the number of subfolders will grows up very quickly(aroud 500 more per day). So I checked carefull if I use TStringList.Free procedure. But still, every time when this procedure is called (around 1 time per 1 minute), I can see the used memory growing quickly (90MByte more after each calling) in windows task manager. Please help me to check if I made some stupid mistakes. Thanks Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure SearchFile(sRootDir: string);var  Found: integer;  SearchRec: TSearchRec;  i,j:integer;  SiteFolders,DayFolders,HourFolders,AudioFolders: TStringList;begin  SiteFolders:=TStringList.Create;    DayFolders:=TStringList.Create;  HourFolders:=TStringList.Create;  AudioFolders:=TStringList.Create;  DoneFileList.Clear;  AudioFileList.Clear;  DataFileList.Clear;  Found := FindFirst(sRootDir + '\*', faDirectory, SearchRec);      while Found = 0 do  begin    if  (SearchRec.Name <> '.') and  (SearchRec.Name <> '..') then    begin      SiteFolders.Add(sRootDir + '\'+SearchRec.Name);    end;    Found := FindNext(SearchRec);  end;  FindClose(SearchRec);   for i:=0 to SiteFolders.Count-1 do  begin    Found := FindFirst(SiteFolders[i]+'\*', faDirectory, SearchRec);       while Found = 0 do    begin      if  (SearchRec.Name <> '.') and  (SearchRec.Name <> '..') then      begin        DayFolders.Add(SiteFolders[i]+'\'+SearchRec.Name);      end;      Found := FindNext(SearchRec);    end;  end;  FindClose(SearchRec);   for i:=0 to DayFolders.Count-1 do  begin    Found := FindFirst(DayFolders[i]+'\*', faDirectory, SearchRec);         while Found = 0 do    begin      if  (SearchRec.Name <> '.') and  (SearchRec.Name <> '..') then      begin        HourFolders.Add(DayFolders[i]+'\'+SearchRec.Name);      end;      Found := FindNext(SearchRec);    end;  end;  FindClose(SearchRec);   for i:=0 to HourFolders.Count-1 do  begin    Found := FindFirst(HourFolders[i]+'\*', faAnyFile, SearchRec);    while Found = 0 do    begin      if (SearchRec.Attr = faDirectory) then      begin       if (SearchRec.Name = 'Audio') then                     begin         AudioFolders.Add(HourFolders[i]+'\'+SearchRec.Name);         end;      end      else begin                                           for j:=0 to DATA_TYPES.Count-1 do        begin          if (DATA_TYPES[j]+'.bid' = SearchRec.Name) then                                  if FileExists(HourFolders[i]+'\'+SearchRec.Name+'.done') then                    begin              DoneFileList.Add(HourFolders[i]+'\'+SearchRec.Name+'.done');          end            else begin              DataFileList.Add(HourFolders[i] + '\' + SearchRec.Name);              Inc(DataFileCount);            end;        end;      end;      Found := FindNext(SearchRec);    end;  end;  FindClose(SearchRec);   for i:=0 to AudioFolders.Count-1 do  begin    Found := FindFirst(AudioFolders[i]+'\*', faAnyFile, SearchRec);    while Found = 0 do    begin      if RightStr(SearchRec.Name,4)='.bid' then                                    if FileExists(AudioFolders[i]+'\'+SearchRec.Name+'.done') then            begin          DoneFileList.Add(AudioFolders[i]+'\'+SearchRec.Name+'.done');        end        else begin          AudioFileList.Add(AudioFolders[i] + '\' + SearchRec.Name);        end;      Found := FindNext(SearchRec);    end;  end;  FindClose(SearchRec);   SiteFolders.Free;  DayFolders.Free;  HourFolders.Free;  AudioFolders.Free;end;           DoneFileList, DataFileList and AudioFolders are global vars. ---------------------------------------------------------------------- bytebites · 2020-04-26 18:48 Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i    for i:=0 to SiteFolders.Count-1 do  begin    Found := FindFirst(SiteFolders[i]+'\*', faDirectory, SearchRec);        while Found = 0 do    begin      if  (SearchRec.Name <> '.') and  (SearchRec.Name <> '..') then      begin        DayFolders.Add(SiteFolders[i]+'\'+SearchRec.Name);      end;      Found := FindNext(SearchRec);    end;    FindClose(SearchRec);<-- move within for loop  end; ---------------------------------------------------------------------- flowCRANE · 2020-04-26 19:19 Shorter, safer and more readable usage of the FindFirst, FindNext and FindClose looks like this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i if FindFirst({parameters}) = 0 thentry  repeat    {usage of the found item}  until FindNext({parameter}) <> 0;finally  FindClose({parameter});end; Follow this schema — you will avoid memory leaks. ---------------------------------------------------------------------- senglit · 2020-04-27 04:25 Quote from: bytebites on April 26, 2020, 08:48:49 pmCode: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i    for i:=0 to SiteFolders.Count-1 do  begin    Found := FindFirst(SiteFolders[i]+'\*', faDirectory, SearchRec);        while Found = 0 do    begin      if  (SearchRec.Name <> '.') and  (SearchRec.Name <> '..') then      begin        DayFolders.Add(SiteFolders[i]+'\'+SearchRec.Name);      end;      Found := FindNext(SearchRec);    end;    FindClose(SearchRec);<-- move within for loop  end;   It works perfectly! Thank you. But I'm still confused that even I put this FindClose at wrong pace, I still called it before the procedure is finished. So, the used memory will growing when the procedure is called, but it should be released before the 'end;'. Maybe there is something I don't know about TSearchRec? ---------------------------------------------------------------------- senglit · 2020-04-27 04:26 Quote from: furious programming on April 26, 2020, 09:19:03 pmShorter, safer and more readable usage of the FindFirst, FindNext and FindClose looks like this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i if FindFirst({parameters}) = 0 thentry  repeat    {usage of the found item}  until FindNext({parameter}) <> 0;finally  FindClose({parameter});end; Follow this schema — you will avoid memory leaks. Yes it looks more beautifull indeed. Thanks! ---------------------------------------------------------------------- PascalDragon · 2020-04-27 07:22 Quote from: senglit on April 27, 2020, 06:25:06 amBut I'm still confused that even I put this FindClose at wrong pace, I still called it before the procedure is finished. So, the used memory will growing when the procedure is called, but it should be released before the 'end;'. Maybe there is something I don't know about TSearchRec? Each call to FindFirst results in the creation of a new TSearchRec. The function does not care about what you pass in, so a previously initialized TSearchRec will leak. As you did so in your loop each loop run except for the last you leaked a TSearchRec. ---------------------------------------------------------------------- flowCRANE · 2020-04-27 12:55 In addition, if you iterate over all the elements of a given collection (e.g. list or array), you can use the for in loop — it will be easier to use the iterator, and the code itself will be shorter. So, in summary, you can do it in this way: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var  Folder: String; {..} for Folder in SiteFolders do  if FindFirst(Folder + '\*', faDirectory, SearchRec) = 0 then  try    repeat      if (SearchRec.Name <> '.') and (SearchRec.Name <> '..') then        DayFolders.Add(Folder + '\' + SearchRec.Name);    until FindNext(SearchRec) <> 0;  finally    FindClose(SearchRec);  end; ---------------------------------------------------------------------- senglit · 2020-04-28 06:21 Quote from: furious programming on April 27, 2020, 02:55:32 pmIn addition, if you iterate over all the elements of a given collection (e.g. list or array), you can use the for in loop — it will be easier to use the iterator, and the code itself will be shorter. So, in summary, you can do it in this way: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var  Folder: String; {..} for Folder in SiteFolders do  if FindFirst(Folder + '\*', faDirectory, SearchRec) = 0 then  try    repeat      if (SearchRec.Name <> '.') and (SearchRec.Name <> '..') then        DayFolders.Add(Folder + '\' + SearchRec.Name);    until FindNext(SearchRec) <> 0;  finally    FindClose(SearchRec);  end; Rewrote the code following your instruction. It looks much beter now. Thanks