记录和内存泄漏

3

请帮我解决这个问题。这是我的代码。我存储了(在这个例子中)10000个字符串,当我尝试删除它们时,只有一部分内存被释放,其余的内存泄漏了。

type
  PMyData = ^TMyData;
  TMyData = record
  Name:    string;
end;
////////

var
XList:Tlist;
//////////

// Here is how I add//
var
 MyData: PMyData;
 I:Integer;
begin
 for I:=0 to 10000 do begin
 New(MyData);
  MyData.Name:='Hello';
   XList.Add(TObject(MyData));
  end;
end;


///Here is how I delete///
var
 MyData: PMyData;
 I:Integer;
begin
 for I:= XList.Count - 1 downto 0 do begin
 MyData:=PMyData (XList[I]); /// I also used (XList.Items[I])  but the result is the same
  Dispose(MyData);
    XList.Delete(I);
end;
7个回答

6

我看不出那段代码有任何泄漏。你是怎么确定发生了泄漏的呢?你在查看任务管理器吗?如果是这样的话,那不是一种可靠的检测内存泄漏的方式。VCL内存管理器不会将已释放的内存释放回操作系统,而是缓存以供稍后重用。任务管理器显示的是已分配内存,它没有关于应用程序实际管理该内存的概念。


2
更好的说法是,它不会将所有释放的内存全部归还给操作系统。 - Mason Wheeler

5
据我所知,只要你有效地执行清理代码,这段代码就不会泄漏。
要检查代码是否泄漏,唯一的方法是通过在代码中添加以下指令(确保它被执行),请求内存管理器报告任何泄漏情况:
 ReportMemoryLeaksOnShutdown := True;

内存管理器会告诉你,如果你的代码请求的任何内存在应用程序终止时没有被释放,即你的代码存在泄漏。
之后,内存管理器的工作是在终止时将内存归还给操作系统,并且它会正确地执行此操作。
现在,关于为什么任务管理器报告更多未释放的内存,当字符串数量增加时,这是因为内存管理器不会立即将所有先前请求的内存在字符串被释放时全部释放给操作系统,以防稍后再次需要;为了避免再次向操作系统请求,它会保留内存。
正如其他答案所述,任务管理器不是发现内存泄漏的好工具...

1
非常感谢!没有内存泄漏。当我添加字符串然后清除它们时,内存管理器保持安静,但是当我添加而不清除时,它会报告有内存泄漏。所以一切都没问题。谢谢! - Serhiy

5

Windows任务管理器并不是一个可靠的内存使用量测量工具。建议使用开启泄漏跟踪功能的FastMM4内存管理器。


3

你的代码看起来很不错。你所说的只释放了一些内存,而其余的内存泄漏是什么意思?你是否从FastMM获取了内存泄漏报告,或者你只是没有看到任务管理器中的内存使用计数器完全下降?

如果你没有看到程序彻底释放所有内存,那是按设计来的。它会保留一些内存,以便如果你分配更多的内存,它不必立即向Windows请求更多的内存,这有助于提高性能。只有在内存管理器实际回收它之前,内存才会“泄漏”。


好的。谢谢您的帮助!我也相信没有内存泄漏,但仍然存在一些问题。当我添加200000个字符串时,这种情况更加明显。例如,如果我第一次添加200000个字符串,我的应用程序使用的内存(根据Windows任务管理器:“Mem Usage”-15304k和“VM Size”-13492k),我尝试释放内存,现在我的应用程序使用:7616k / 5800k。 如果我添加两倍的字符串(400000),我的应用程序现在使用的是:25480k / 23436k,在我释放它后是8824k / 6768k,这比第一次释放时的值(7616k / 5800k)更高。所以,这是正常的吗?似乎添加的字符串越多,内存泄漏的情况就越严重? - Serhiy
2
如果你想知道是否真的有内存泄漏,可以在你的 DPR 中加入 ReportMemoryLeaksAtShutdown := true;,并查看当你退出程序时是否报告了任何内容。 - Mason Wheeler

0
在Dispose或将记录更改为class(TObject)之前调用以下函数:
Finalize(MyData^);
祝好

Dispose方法会为您执行Finalize操作,因此不需要调用Finalize方法。 - The_Fox

0

每当记录指针包含一个 Variant、未绑定的数组,例如...数组类型的字符串,除了短字符串类型和接口时,您需要在进行 FreeMem 或 Dispose 之前调用 Finalize 以释放这些数据。否则使用类更好。

祝好


0

尝试:

FreeAndNil(XList.Items[I]); 
XList.Delete(I);

好的。我尝试了,但那样不行。我找到的唯一解决办法是使用 XList.Clear,但这样会清除整个列表,但如果我只想删除一些项而不是全部怎么办?XList.Delete(I) 不起作用,它只会在 TList 中删除一个项目但不释放内存?尽管 XList.Clear 确实可以删除项目并释放内存! - Serhiy
不行,我出错了: 无法将常量对象作为var参数传递。如果我这样做 MyData:= PMyData(XList [I]); ///// FreeAndNil(MyData); 而不是你的变量,它可以工作,但只能使用一次。如果我再试着添加字符串,我会得到错误信息。 - Serhiy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接