释放的 TStringList 在 FastMM4 报告中算作内存泄漏的原因

Reason why a freed TStringList counts as memory leak in the FastMM4 report

在我的一个应用程序的任务管理器中注意到奇怪的内存行为后,我开始使用 FastMM4 调查内存泄漏。 刚打开三个表格就发现了1000多个。 我希望我能在一个地方找到原因

在一种情况下,即使始终释放 finally 块中的 TStringList(始终到达),FastMM4 也会报告内存泄漏:

function GetMatchingResourceFileName(MatchingString:string) : string;
var
  ResourcesList: TStringList;
  I : Integer;
begin
  Result := '';
  try
    ResourcesList := TStringList.Create;
    // get resource files list
    ResourcesList := GetResourceList;
    //search for matching file name
    for I := 0 to ResourcesList.Count-1 do
    begin
      if Pos(MatchingString,ResourcesList.Strings[I]) > 0 then
      begin
        Result := ResourcesList.Strings[I];
        break;
      end;
    end;
  finally
    ResourcesList.Free;
    ResourcesList:= nil;
  end;
end;

FastMM4 堆栈报告告诉我泄漏从

开始
ResourcesList := TStringList.Create;

即使我 100% 确定 ResourcesList.Free; 已执行,我仍然看到内存泄漏。

这里可以看到断点被命中:

当我关闭应用程序时,我看到了报告:

---------------------------
myProject.exe: Memory Leak Detected
---------------------------
This application has leaked memory. The small-block leaks are (excluding expected leaks registered by pointer):



85 - 100 bytes: System.Classes.TStringList x 1



Note: Memory leak detail is logged to a text file in the same folder as this application. To disable this memory leak check, undefine "EnableMemoryLeakReporting".

为了研究上面的泄漏,我评论说我 99% 的代码都集中在第一个报告的泄漏上,事实上,一个在应用程序的初始化部分。

这怎么可能?

更新

代码的工作版本避免调用 TStringList.Create,因为 GetResourceList 方法已经 returns 正确创建 TStringList,以下代码现在没有泄漏:

function GetMatchingResourceFileName(MatchingString:string) : string;
var
  ResourcesList: TStringList;
  I : Integer;
begin
  Result := '';
  try
    ResourcesList := GetResourceList;
    //search for matching file name
    for I := 0 to ResourcesList.Count-1 do
[...]

你有两个问题:

1

1.    ResourcesList := TStringList.Create;
2.    // get resource files list
3.    ResourcesList := GetResourceList;

在第 1 行,您创建了一个新的 TStringList 对象并将地址保存到本地 ResourcesList 变量中。

但是在第 3 行,我想 GetResourceList 函数也创建了一个新的 TStringList 对象,然后你重写局部 ResourcesList 变量,让它指向这个,新的, 反对。

这意味着现在没有变量 指向您创建的第一个TStringList 对象。因此,它永远被泄露了。

你要的是这个:

    // get resource files list
    ResourcesList := GetResourceList;

2

你的代码本质上是

try
  ResourcesList := TStringList.Create; //or GetResourceList;
  // Use the list
finally
  ResourcesList.Free
end;

这是一个非常常见的错误。如果 TStringList.Create 构造函数失败(或 GetResourceList 函数),部分创建的 TStringList 对象将自动释放(或希望由 GetResourceList 函数释放),但随后出现异常停止执行,因此没有值写入 ResourcesList.

因此,ResourcesList.Free 将 运行 随机指针上的析构函数,因为未初始化非托管类型的局部变量。

必须

ResourcesList := TStringList.Create; //or GetResourceList;
try
  // Use the list
finally
  ResourcesList.Free
end;