FastMM4 报告中释放的 TStringList 被视为内存泄漏的原因

LaB*_*cca 2 delphi memory-leaks tstringlist fastmm delphi-2010

在注意到我的一个应用程序的任务管理器中出现奇怪的内存行为后,我开始使用 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;
Run Code Online (Sandbox Code Playgroud)

FastMM4 堆栈报告告诉我泄漏是从

ResourcesList := TStringList.Create;
Run Code Online (Sandbox Code Playgroud)

即使我 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".
Run Code Online (Sandbox Code Playgroud)

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

这怎么可能?

更新

代码的工作版本避免了调用TStringList.Create,因为该GetResourceList方法已经返回了正确创​​建的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
[...]
Run Code Online (Sandbox Code Playgroud)

And*_*and 9

你有两个问题:

1

1.    ResourcesList := TStringList.Create;
2.    // get resource files list
3.    ResourcesList := GetResourceList;
Run Code Online (Sandbox Code Playgroud)

在第 1 行,您创建一个新TStringList对象并将其地址保存在局部ResourcesList变量中。

但在第 3 行,我认为该GetResourceList函数还创建了一个新TStringList对象,然后重写局部ResourcesList变量,使其指向这个新对象。

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

你想要的是这样的:

    // get resource files list
    ResourcesList := GetResourceList;
Run Code Online (Sandbox Code Playgroud)

2

你的代码本质上是

try
  ResourcesList := TStringList.Create; //or GetResourceList;
  // Use the list
finally
  ResourcesList.Free
end;
Run Code Online (Sandbox Code Playgroud)

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

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

必须

ResourcesList := TStringList.Create; //or GetResourceList;
try
  // Use the list
finally
  ResourcesList.Free
end;
Run Code Online (Sandbox Code Playgroud)