String Delphi应用程序泄漏分析
根据FastMM4,我目前正在开发的Delphi程序正在泄漏大量字符串。确切地说,答案是: 应用程序()过去泄漏了更多的其他数据类型,但是FastMM4可以报告实例的创建位置,所以我设法解决了这个问题。奇怪的是,FastMM4根本不报告这些泄漏的位置 Edit:看来确实如此,请参见解决方案。无论如何,问题仍然存在:我到底是怎么泄露这些东西的? 所以,嗯,不幸的是,我不知道该找什么。我的意思是,如果这些东西超出范围,它们应该被自动释放(即使它们在堆上) 我确实设法通过随机评论和观察计数的变化来追踪一些泄漏。下面是一个例子:String Delphi应用程序泄漏分析,string,delphi,memory,memory-leaks,dev-c++,String,Delphi,Memory,Memory Leaks,Dev C++,根据FastMM4,我目前正在开发的Delphi程序正在泄漏大量字符串。确切地说,答案是: 应用程序()过去泄漏了更多的其他数据类型,但是FastMM4可以报告实例的创建位置,所以我设法解决了这个问题。奇怪的是,FastMM4根本不报告这些泄漏的位置 Edit:看来确实如此,请参见解决方案。无论如何,问题仍然存在:我到底是怎么泄露这些东西的? 所以,嗯,不幸的是,我不知道该找什么。我的意思是,如果这些东西超出范围,它们应该被自动释放(即使它们在堆上) 我确实设法通过随机评论和观察计数的变化来追
// simply passing it a constant creates a leak...
MainForm.UpdateSplash('Creating extra dialogs...');
procedure TMainForm.UpdateSplash(const text : AnsiString);
begin
if not devData.NoSplashScreen then // even if this branch is NOT taken
SplashForm.Statusbar.SimpleText := 'blablabla' + text;
end;
// And even if the function call itself is placed within a NOT taken branch!
下面是另一个泄漏示例:
// Passing this constants produces leaks...
procedure TCodeInsList.AddItemByValues(const a, b, c: AnsiString;...);
var
assembleditem : PCodeIns;
begin
new(assembleditem);
assembleditem^.Caption:=a;
assembleditem^.Line:=b;
assembleditem^.Desc:=c;
...
fList.Add(assembleditem);
end;
// ... even when calling this on WM_DESTROY!
destructor TCodeInsList.Destroy;
var
I: integer;
begin
for I := 0 to fList.Count - 1 do
Dispose(fList[I]);
fList.Free;
inherited Destroy;
end;
// produces leaks!?
这里有相当多的字符串泄漏问题,但没有一个真正澄清人们应该寻找什么模式。谷歌也没有提供
Edit:因此,我必须查找传递的常量。但是为什么呢?
那么,嗯,有什么想法吗?你说得对,字符串应该自动清理。不过,我已经找到了一些方法把事情搞砸 第一种情况是,如果直接使用字符串数据结构进行操作,可能会破坏引用计数。这是最有可能的,因为您泄漏的字符串数量
另一个调用Halt并在堆栈上保留字符串引用。但您不会在堆栈上留下40000个字符串引用,因此我会查找传递字符串的代码,然后调整其引用计数。您不需要显式分配字符串。除了破坏引用计数外,对象或记录的字符串字段也可能泄漏。比如说,
type
PRecord = ^TRecord;
TRecord = record
S: string;
end;
procedure TForm1.Button4Click(Sender: TObject);
var
r: PRecord;
begin
GetMem(r, SizeOf(r^));
Initialize(r^);
r.S := ' ';
FreeMem(r);
在上面的例子中,由于记录本身的内存被释放,FastMM将只报告泄漏的字符串
在任何情况下,FastMM在对话框中不显示堆栈跟踪并不意味着它缺少该信息。确保在“FastMM4Options.inc”中定义了
FullDebugMode
、LogMemoryLeakDetailToFile
和LogErrorsToFile
。然后在可执行文件的目录中查找“[ExecutableName]\u MemoryManager\u EventLog.txt”文件
对于上述示例,FastMM生成以下文件:
--------------------------------2012/5/27 4:34:46--------------------------------
A memory block has been leaked. The size is: 12
Stack trace of when this block was allocated (return addresses):
40305E
404B5D
404AF0
45C47B
43D726
42B0C3
42B1C1
43D21E
76C4702C [GetWindowLongW]
77AE3CC3 [Unknown function at RtlImageNtHeader]
The block is currently used for an object of class: Unknown
The allocation number is: 484
Current memory dump of 256 bytes starting at pointer address 7EF8DEF8:
01 00 00 ...
...
对于要处理的记录,代码应将指针类型转换为其类型:
Dispose(PCodeIns(p));
因此,您的“TCodeInsList.Destroy”应该是:
destructor TCodeInsList.Destroy;
var
I: integer;
begin
for I := 0 to fList.Count - 1 do
Dispose(PCodeIns(fList[I]));
fList.Free;
inherited Destroy;
end;
最后,您要寻找的模式似乎是寻找代码打算释放具有字符串字段的记录(不太可能的对象)的位置。查找
Dispose
,不太可能FreeMem
,甚至不太可能FreeInstance
释放FastMM在分配内存泄漏时显示的对象/记录的内存,这可能会有所帮助。对于短词,Delphi内置字符串类型是引用计数的。内存分配和dispose方法不负责更新引用计数,因此编译器不知道记录中的字符串实际上可以被释放
不鼓励使用引用计数的字符串类型定义记录。我以前也有过同样的困惑。如果您查看一下Delphi库的源代码。您会发现许多记录的PChar不是字符串
泄漏字符串的最常见方法是有一个包含字符串的记录和指向该记录的指针。如果只对该指针执行Dispose(),编译器将释放该指针,而不是下一条记录中的所有内容。始终确保您的dispose代码告诉编译器您要处理的内容 例如,假设在TTreeView中,我将
PMyRecord=^MyRecord
放在Node.Data中。如果在最后循环遍历所有节点并只执行Dispose(Node.Data)
,则MyRecord中的任何字符串都将无法正确处理
但是,如果通过调用
dispose(PMyRecord(Node.Data))
明确地告诉编译器指针的基本类型来处理指针,那么就不会有内存泄漏。我发现,即使没有内存分配/指针操作,字符串(作为记录中的字段)也会泄漏
听起来很疯狂,但这是真的,至少在XE3中是这样。以下是一个例子:
TMyRecord = record
x,
y: integer;
s: ansistring;
end;
function GetMyRec: TMyRecord;
begin
....
end;
....
procedure DoSomething;
var
rec: TMyRecord;
begin
...
rec := GetMyRec; //First call - everything is OK
...
rec := GetMyRec; //Repeated call > Memory Leak of
//Ansistring !!!!
//To avoid the leak do the following BEFORE a
//repeated call: rec.s := unassigned;
end;
我现在无法加载sourceforge项目。是否有任何可能,主要形式没有被适当地破坏,从而留下悬垂的弦?这样行吗?delphi版本?如果可以,请使用aqtime进行测试,它将准确地告诉您泄漏的位置。@RichardA:正如您在source\devcpp.dpr中看到的,splashform是使用“Free”释放的。将尝试将caFree添加到OnClose事件。@沃伦:我非常怀疑aqtime能否告诉我比FastMM4、gpProfiler和MemCheck更多的东西。我也需要升级到XE(现在使用D7)。我的大学确实有一个D2009许可证在某处浮动(虽然不适用于像我这样的EE人员),但aqtime甚至不支持它。aqtime在旧的delphi版本中运行良好,但是的,你必须购买它。AQTime在Delphi7到XE2之间运行。我百分之百确定我没有在任何地方使用Halt。我经常使用Exit,但这应该没什么大不了的。嗯,不,我不是在搞第0个索引。@Orwell
exit
可以安全地在任何地方使用:它将转到隐藏的try,FastMM4确实显示了一些信息:102DF8[SynEditKeyCmds][SynEditKeyCmds][@GetMem]。将其乘以40000,在一个150MiB的文本文件中得到这个想法。谢谢,我会调查的。顺便说一句,如果你告诉链接者去做,那工作就容易多了
destructor TCodeInsList.Destroy;
var
I: integer;
begin
for I := 0 to fList.Count - 1 do
Dispose(PCodeIns(fList[I]));
fList.Free;
inherited Destroy;
end;
TMyRecord = record
x,
y: integer;
s: ansistring;
end;
function GetMyRec: TMyRecord;
begin
....
end;
....
procedure DoSomething;
var
rec: TMyRecord;
begin
...
rec := GetMyRec; //First call - everything is OK
...
rec := GetMyRec; //Repeated call > Memory Leak of
//Ansistring !!!!
//To avoid the leak do the following BEFORE a
//repeated call: rec.s := unassigned;
end;