Delphi应用程序泄漏AnsiStrings

Orw*_*ell 11 memory delphi string memory-leaks dev-c++

根据FastMM4,我正在研究的Delphi程序正在泄漏很多字符串.确切地说,AnsiStrings:

在此输入图像描述

应用程序(http://sourceforge.net/projects/orwelldevcpp/)曾经泄漏了更多其他数据类型,但FastMM4可以报告实例的创建位置,因此我设法解决了这个问题.奇怪的是,FastMM4根本不报告这些泄漏的位置.

编辑:它似乎确实存在,看到修复的答案.无论如何,问题仍然存在:我在世界上如何泄漏这些东西?

所以,嗯,不幸的是,我不知道该寻找什么.我的意思是,如果这些东西超出范围,它们应该自动释放(即使它们在堆上)?

我确实设法通过随机评论跟踪一些泄漏事件并查看计数会发生什么.这是一个例子:

// 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!
Run Code Online (Sandbox Code Playgroud)

这是泄漏的另一个例子:

// 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!?
Run Code Online (Sandbox Code Playgroud)

这里有相当多的字符串泄漏问题,但没有一个真正阐明应该寻找的模式.谷歌也没有提供.

编辑:所以,我必须寻找传递的常量.但为什么?

嗯,任何想法?

Ser*_*yuz 14

您不需要显式分配字符串.除了使用引用计数进行修改之外,对象或记录的字符串字段也可能泄漏.例如,

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);
Run Code Online (Sandbox Code Playgroud)

在上面的示例中,由于释放了记录本身的内存,因此FastMM将仅报告泄漏的字符串.


在任何情况下,FastMM都没有在对话框中显示堆栈跟踪并不意味着它缺少该信息.一定要有FullDebugMode,LogMemoryLeakDetailToFileLogErrorsToFile在'FastMM4Options.inc'中定义.然后在可执行文件的目录中查找"[ExecutableName] _MemoryManager_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 ...
...

现在您可以运行该应用程序,暂停它,然后搜索地址.对于上述日志和测试应用程序,地址解析为:

Stack trace of when this block was allocated (return addresses):
40305E    -> _GetMem
404B5D    -> _NewAnsiString
404AF0    -> _LStrAsg
45C47B    -> TForm1.Button4Click (on FreeMem line)
43D726    -> TControl.Click
... 


编辑: 不是手动查找地址,而是通过链接器选项生成详细的地图文件,而FastMM会这样做(感谢Mason的评论).


您对该问题的编辑反映了类似于上述示例中的泄漏.如果'fList'是常规的TList,它只保存指针并且不知道这些指针指向的是什么.因此,当您释放指针时,只释放为指针本身分配的内存,而不释放记录的字段.因此泄漏无关传递给函数的常量,但就像下面的模式:

var
  assembleditem: PCodeIns;
  p: Pointer;
begin
  new(assembleditem);
  assembleditem^.Caption:='a';
  ..    
  p := assembleditem;
  Dispose(p);
Run Code Online (Sandbox Code Playgroud)

对于要处理的记录,代码应该将指针指向其类型:

Dispose(PCodeIns(p));
Run Code Online (Sandbox Code Playgroud)

所以'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;
Run Code Online (Sandbox Code Playgroud)


最后,您正在寻找的模式似乎正在寻找代码意图释放具有字符串字段的记录(不太可能的对象)的位置.寻找Dispose,不太可能FreeMem,甚至不太可能FreeInstance释放FastMM显示的对象/记录的内存,因为分配的内存泄露可能会有所帮助.