【问题标题】:delphi string leakdelphi 字符串泄漏
【发布时间】:2011-03-24 17:50:59
【问题描述】:

我正在使用 Delphi XE,并编写一个使用 RemObjects SDK 进行通信的应用程序(如果可能相关)。我启用了 FastMM 调试,有时(并非总是)当我关闭它时会发出有关单个“意外内存泄漏”的警告。 “发生了意外的内存泄漏。意外的小块泄漏是:117-124 字节:UnicodeString x 1”。偶尔,我会收到 x2 报告。

现在,我的理解是字符串是引用计数的,并且由于没有其他对象会导致泄漏,那么可能导致这种情况发生的情况是什么?在this StackOverflow question 中,人们找不到泄密的方法。

如果没有明显的办法,那我就下载最新的FastMM源码(XE源码好像不包含)。

[解决后编辑] 找到此问题的解决方案是安装 FastMM 源,并启用 FullDebugMode 以获取堆栈跟踪。

【问题讨论】:

  • 我发现 AQTime 在查找内存泄漏方面比 Fast MM 完整调试模式更有用。正是由于 FastMM 报告此类泄漏的方式(首先告诉您有关字符串泄漏,而不是包含泄漏的字符串内存的对象),我更喜欢 AQTime。
  • @Warren 没有 FastMM 也告诉你拥有该字符串的对象。那是我的回忆,但我已经很久没有内存泄漏了!
  • @jachguate 问题就在问号之前。 @David,没有提到其他项目。当我确实泄漏了一个类时,我得到了类类型和它包含的任何字符串。
  • 按照我在回答中所说的方式进行操作,您也会获得堆栈跟踪。我不记得设置了,但我相信你可以从 FastMM 的文档中解决。

标签: delphi delphi-xe


【解决方案1】:

当使用类型化常量并根据最终化顺序时,FastMM 过去可能会在确实没有泄漏时报告泄漏。

FastMM: Leaked memory reported where I believe it shouldn't.

简而言之,当 FinalizedFirst 单元 得到最终确定,SString 常量 得到释放。最终确定后 单元完成,完成 FinalizedLast 被调用。其中是 完成,它调用的方法 FinalizedFirst 的 LeakMemory 方法。 SString 变量得到 再次初始化并且没有得到 释放为最终确定 FinalizedFirst 已经运行。

最后一个单元

unit FinalizedLast;    

interface

uses FinalizedFirst;

implementation

initialization LeakMemory;
finalization LeakMemory;
end.

FinalizedFirst Unit

unit FinalizedFirst;

interface

procedure LeakMemory;

implementation

uses FinalizedLast;

procedure LeakMemory;
const
  SString: string = '';
begin
  //***** SString will get initialized once or twice depending on the
  // finalization order of units. If it get's initialized twice,
  // a memory leak is reported.
  if SString = '' then
  SString := 'FooBar';
end;
end.

LeakMemory 项目

program LeakMemory;
uses
FastMM4 in 'FastMM4.pas',
Forms,
FinalizedFirst in 'FinalizedFirst.pas',
FinalizedLast in 'FinalizedLast.pas';

{$R *.RES}
begin

Application.Initialize;
Application.Run;

end.

【讨论】:

    【解决方案2】:

    您可以通过使用 FreeMem 而不是 Dispose 释放堆上的记录或使用 System.Move 或 FillChar 覆盖记录来泄漏字符串。在第一种情况下,最终代码没有运行,而在第二种情况下,如果字符串字段被填充为 nil,它会认为它已经被清除了。

    如果您想找到泄漏的位置,请下载 FastMM 并打开 FullDebugMode。它将包括泄漏发生位置的堆栈跟踪。

    【讨论】:

    • 我在这里没有做任何花哨的事情 - 没有记录,也没有手动内存管理。
    • 一个未被释放的类对象,其中包含字符串?一个动态的字符串数组,你没有 SetLength(array,0)?
    • @Warren:第一个也会将该类显示为泄漏。动态数组也是引用计数的,所以第二个无关紧要。
    • @mj2008:如果您在终结块中使用它们,我隐约记得其中一个 Delphi 版本(2006 年?)泄漏了全局 var 块中的字符串。应该是 2007 年或更早的时候,我已经很久没有遇到它了,所以如果是 2007 年,它肯定已经在更新中修复了。
    【解决方案3】:

    唯一能想到的在不故意破坏字符串的情况下泄漏字符串(例如手动增加引用计数或执行一些混乱的指针操作)的方法是使用 threadvar。

    就像帮助文件状态一样,

    通常是动态变量 由编译器管理(长字符串, 宽字符串,动态数组, 变体和接口)可以是 用 threadvar 声明,但 编译器不会自动释放 创建的堆分配内存 每个执行线程。如果你使用 线程变量中的这些数据类型, 您有责任处理 他们的记忆从内部 线程,在线程终止之前。

    除此之外,没有什么是尚未说明的。

    [Edit by questioner]确实是这个问题,具体代码如下:

    threadvar
        g_szAuthentication : String;
    
    
    procedure TMyBase.SetAuthentication(szUserName, szPassword: String);
    begin
        g_szAuthentication := '?name=' + szUserName + '&pass=' + szPassword;
    end;
    

    【讨论】:

      【解决方案4】:

      当字符串包含在其他未正确销毁的对象中时,我通常会看到字符串泄漏。例如尚未释放的对象。但是,您会希望看到该对象也会被报告。

      解决此问题的方法是下载并使用完整版 FastMM,并将其配置为在检测到泄漏时报告堆栈跟踪。当您这样做时,您将获得分配泄漏对象的代码的完整堆栈跟踪,此时通常很清楚问题所在。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-04-30
        • 2011-08-29
        • 2013-04-03
        • 2011-06-12
        • 2018-12-17
        • 2013-12-03
        • 2011-05-04
        相关资源
        最近更新 更多