【问题标题】:Delphi 2009 - Can an Interface Property Cause a Memory Leak?Delphi 2009 - 接口属性会导致内存泄漏吗?
【发布时间】:2009-06-24 00:13:28
【问题描述】:

我继承了一个 Intraweb 应用程序,该应用程序有一个 2MB 的内存泄漏文本文件,由 FastMM4 报告。我已经把它归结为一个类的 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)

【问题讨论】:

  • 等一下……你在说什么接口属性?这里没有属性。
  • 我们需要 GetAdapterNav 的代码,以查看对象/接口是如何创建的。另外,调用它的代码,看看它是如何被处理的。
  • 是的,你是对的。它不是属性,而是检索适当接口实现的内部工作者。
  • 抱歉,发布函数时遇到问题。
  • 函数 TCwcBasicAdapter.GetAdapterNav(aDataSet: TDataSet): ICwcCDSAdapterNav; var AdapterNav: TCwcCDSAdapterNavBase;开始结果 := nil;如果已分配(aDataSet),则开始 AdapterNav := TCwcCDSAdapterNavBasic.Create(aDataSet, FKeyField.Index, FNavTitleField.Index);尝试 AdapterNav.GetInterface(ICwcCDSAdapterNav,结果);除了 FreeAndNil(AdapterNav);增加;结尾;结尾;结尾;类声明为:TCwcCDSAdapterNavBase = class(TInterfacedObject, ICwcCDSAdapterNav)

标签: delphi interface memory-leaks delphi-2009 intraweb


【解决方案1】:

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

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

编辑:没有看到您的 Object 实现 ICwcCDSAdapterNav 的代码,我们无法判断它是否确实是引用计数的。
如果您不是从 TInterfacedObject 继承的 strong>,有可能它没有被引用计数并且你不能依赖这种自动释放...

您可能想看看这个 CodeRage 2 会话Fighting Memory Leaks for Dummies。它主要展示了如何使用 FastMM 来防止/检测 Delphi 中的内存泄漏。适用于 D2007,但仍适用于其他版本。

【讨论】:

  • 谢谢,今天早上我会在工作时查看您的演示文稿。你在 DelphiLive 上被裁掉了!。
  • 您是否有介绍“绕过泄漏容器”幻灯片中列出的一些技术的演示文稿?另外,我下载了你的 DelphiLive!演示文稿,但看不到查看它的方法。是不是文件丢失了?
  • 我选择了这个答案,因为引用的演示文稿帮助我找到了真正的问题,尽管还没有解决。
【解决方案2】:

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

  1. 仅当接口所属的对象在其 _AddRef 和 _Release 方法中实现了引用计数时,接口才会被引用计数。有些对象没有。
  2. 如果您有循环接口引用(接口 1 引用接口 2,它引用接口 1),那么如果您没有一些特殊技巧,引用计数将永远不会降到 0。如果这是您的问题,我会将您推荐给 Andreas Hausladen 的recent blog post on the subject.

【讨论】:

  • 谢谢,我怀疑上面的第 2 项正在发生。
  • 您还可以检查奇怪的用法,例如演员表。对于追踪泄漏,FastMM 肯定有帮助,但我建议你试试 AQTime automatedqa.com/products/aqtime。我知道它不是免费的,但知道每次泄漏的完整堆栈跟踪肯定会缩短清理代码所需的时间。
  • @Mason- 你记得文章的名字吗?链接现已断开。
【解决方案3】:

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

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

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

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


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

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

if Assigned(FDataSet) then
  Result := TCwcCDSAdapterNavBase.Create(...);

【讨论】:

  • 看起来他已经获得了有关泄露内容的日志文件。但是知道它在哪里被构建和知道什么时候摧毁它之间的关系并不总是一个简单或明显的关系。
  • 如果您知道谁创建了泄露的对象,那么这通常会告诉您谁应该负责释放它们。而且我知道他已经有关于泄漏的日志。但我建议他可能没有查看整个日志,因为不太可能只有一个类的实例泄漏。类为自己分配的东西可能也泄露了,以及那些接口引用指向的对象。最后,重点是接口属性不能是该类实例泄漏的原因。
  • FastMM4 报告引用了泄漏类的创建,它被添加到 TObject 后代的 TObjectList 成员中。 TObjectList 被创建为非拥有,并且在对其项目调用 Remove 的 for 循环后在类的析构函数中为 FreeAndNil。令人沮丧的部分是一切都按预期发生。
  • 那么,如果 TObjectList 不拥有这些创建的对象,那么谁拥有呢?负责清理这些对象的代码是什么?你有任何代码可以释放他们吗?如果没有,则添加一些(这可能就像让列表拥有对象一样简单)。如果是这样,那么找出为什么该代码没有被执行。
  • 这可能是一个非常糟糕的主意,具体取决于。如果他依靠引用计数系统来释放它们,并且其中一些被正确处理,因此泄漏的 115 不是该列表中的所有实例,那么将列表设置为拥有这些对象将导致加倍- 无错误。
猜你喜欢
  • 2021-03-23
  • 2021-09-25
  • 2014-12-08
  • 1970-01-01
  • 1970-01-01
  • 2010-09-09
  • 2011-06-16
  • 2011-10-28
相关资源
最近更新 更多