4

我继承了一个 Intraweb 应用程序,该应用程序具有 FastMM4 报告的 2MB 内存泄漏文本文件。我已经把它归结为一个类的 115 个实例泄漏 52 个字节。

对坏演员的简要描述是:

TCwcBasicAdapter = class(TCwcCustomAdapter)  
  protected  
    FNavTitleField: TField;  
    function GetAdapterNav(aDataSet: TDataSet): ICwcCDSAdapterNav; override;  
  public  
    constructor Create(aDataSource: TDataSource; aKeyField, aNavTitleField: TField; aMultiple: boolean);  
  end;  

界面是:

  ICwcCDSAdapterNav = interface(IInterface)  

我是不是在叫错树,因为该属性是引用计数的?是否存在接口属性可以防止类被破坏的情况?

下面是上述方法的实现:

function TCwcBasicAdapter.GetAdapterNav(aDataSet: TDataSet): ICwcCDSAdapterNav;
var
  AdapterNav: TCwcCDSAdapterNavBase;
begin
  result := nil;
  if Assigned(aDataSet) then begin
    AdapterNav := TCwcCDSAdapterNavBasic.Create(aDataSet, FKeyField.Index, FNavTitleField.Index);
    try
      AdapterNav.GetInterface(ICwcCDSAdapterNav, result);
    except
      FreeAndNil(AdapterNav);
      raise;
    end;
  end;
end;

类声明为:

TCwcCDSAdapterNavBase = class(TInterfacedObject, ICwcCDSAdapterNav)
4

3 回答 3

4

FastMM 应该为您提供泄漏的内容和创建位置。
这将有助于将其缩小到真正的罪魁祸首:谁泄露了什么?

我不确定你的问题到底是什么?
您的代码不完整或不是问题:您的类没有接口属性,也没有接口私有字段,只是一个返回接口的方法,这是无害的。

编辑:在没有看到实现 ICwcCDSAdapterNav 的对象的代码的情况下,我们无法判断它是否确实是引用计数的。
如果您不从 TInterfacedObject 下降则很可能它没有被引用计数,并且您不能依赖这种自动释放...

您可能想看看这个CodeRage 2 会议为傻瓜而战的内存泄漏。它主要展示了如何使用 FastMM 来防止/检测 Delphi 中的内存泄漏。适用于 D2007,但仍适用于其他版本。

于 2009-06-24T00:57:01.977 回答
4

到目前为止,关于 FastMM 的工作原理,您已经得到了一些很好的答案。但至于您的实际问题,是的,接口对象可能以两种不同的方式泄漏。

  1. 只有当接口所属的对象在其 _AddRef 和 _Release 方法中实现了引用计数时,接口才会被引用计数。有些物体没有。
  2. 如果您有循环接口引用(接口 1 引用接口 2,它引用接口 1),那么如果您没有一些特殊技巧,引用计数将永远不会下降到 0。如果这是您的问题,我建议您参考 Andreas Hausladen最近关于该主题的博客文章。
于 2009-06-24T01:20:26.513 回答
2

如果您泄漏了该类的 115 个实例,那么就是该类被泄漏了。该类占用的内存,而不是它所引用的东西占用的内存,正在泄漏。在某个地方,您有 115 个TCwcBasicAdapter没有释放的实例。

此外,属性不存储数据,无论它们是接口还是其他类型。只有字段占用内存(以及编译器代表类分配的一些隐藏空间)。

所以,是的,你在叫错树。您的内存泄漏在其他地方。当 FastMM 告诉您有内存泄漏时,它是否还告诉您每个泄漏实例的分配位置。它具有这种能力;您可能需要调整一些条件编译符号来启用该功能。

当然,泄漏的不仅仅是该类的实例。FastMM 还应该报告其他一些泄漏,例如实现接口的类或类的实例。


根据您添加的功能,我开始怀疑它确实TCwcCDSAdapterNavBase在泄漏,这可能是因为您使用非典型方式创建它。异常处理程序是否GetAdapterNav曾经运行过?我对此表示怀疑; TObject.GetInterface从不明确引发异常。如果对象不支持该接口,则返回False. 异常处理程序可以捕获的只是访问冲突和非法操作之类的东西,无论如何你真的不应该在那里捕获它们。

您可以像这样更直接地实现该功能:

if Assigned(FDataSet) then
  Result := TCwcCDSAdapterNavBase.Create(...);
于 2009-06-24T01:05:25.797 回答