Delphi 2009 - 接口属性会导致内存泄漏吗?

发布于 2024-07-25 17:54:02 字数 1146 浏览 2 评论 0原文

我继承了一个 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)

I inherited an Intraweb app that had a 2MB text file of memory leaks as reported by FastMM4. I've got it down to 115 instances of one class leaking 52 bytes.

A brief description of the bad actor is:

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

and the interface is:

  ICwcCDSAdapterNav = interface(IInterface)  

Am I barking up the wrong tree, since the property is reference counted? Are there any circumstances where the interface property could keep the class from being destroyed?

Here is the implementation of the method above:

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;

with the class declared as:

TCwcCDSAdapterNavBase = class(TInterfacedObject, ICwcCDSAdapterNav)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

忆沫 2024-08-01 17:54:02

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

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

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

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

FastMM should give you what is leaked and where it was created.
That would help narrowing it down to the real culprit: who is leaking what?

I'm not sure what really your question is?
Your code is incomplete or not the one in question: your class does not have an Interface property nor an Interface private Field, just a method that returns an Interface, which is harmless.

Edit: Without seeing the code of your Object implementing ICwcCDSAdapterNav, we can't tell if it is indeed reference counted.
If you don't descend from TInterfacedObject, chances are that it's not reference counted and that you cannot rely on this automagically freeing...

You may want to give a look at this CodeRage 2 session: Fighting Memory Leaks for Dummies. It mainly shows how to use FastMM to prevent/detect memory leaks in Delphi. Was for D2007 but still relevant for other versions.

无语# 2024-08-01 17:54:02

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

  1. 仅当接口所属的对象在其 _AddRef 和 _Release 方法中实现了引用计数时,接口才进行引用计数。 有些物体则不然。
  2. 如果您有循环接口引用(接口 1 引用接口 2,接口 2 又引用接口 1),那么如果您不采取一些特殊技巧,引用计数将永远不会降至 0。 如果这是您的问题,我将向您推荐 Andreas Hausladen 的最近关于该主题的博客文章。

You've got some good answers so far about how FastMM works. But as for your actual question, yes, interfaced objects can leak in two different ways.

  1. Interfaces are only reference-counted if the objects they belong to have implemented reference counting in their _AddRef and _Release methods. Some objects don't.
  2. If you have circular interface references, (Interface 1 references interface 2, which references interface 1,) then the reference count will never fall to 0 without some special tricks on your part. If this is your problem, I'll refer you to Andreas Hausladen's recent blog post on the subject.
美人如玉 2024-08-01 17:54:02

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

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

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

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


根据您添加的函数,我开始怀疑确实是 TCwcCDSAdapterNavBase 发生了泄漏,这可能是因为您创建它时使用的非典型方式所致。 GetAdapterNav 中的异常处理程序是否运行过? 我对此表示怀疑; TObject.GetInterface 从不显式引发异常。 如果对象不支持该接口,则返回False。 所有异常处理程序可以捕获的都是诸如访问冲突和非法操作之类的内容,无论如何您都不应该在那里捕获它们。

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

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

If you are leaking 115 instances of that class, then it is that class that is being leaked. The memory occupied by that class, not the memory occupied by the things it refers to, is being leaked. Somewhere, you have 115 instances of TCwcBasicAdapter that you're not freeing.

Furthermore, properties don't store data, no matter they're interfaces or some other type. Only fields occupy memory (along with some hidden space the compiler allocates on the class's behalf).

So, yes, you are barking up the wrong tree. Your memory leak is somewhere else. When FastMM tells you that you have a memory leak, doesn't it also tell you where each leaked instance was allocated. It has that capability; you might need to adjust some conditional-compilation symbols to enable that feature.

Surely it's not only instances of that class that are leaking, though. FastMM should also report some other things leaking, such as instances of the class or classes that implement the interface.


Based on the function you added, I've begun to suspect that it's really TCwcCDSAdapterNavBase that's leaking, and that could be because of the atypical way you use for creating it. Does the exception handler in GetAdapterNav ever run? I doubt it; TObject.GetInterface never explicitly raises an exception. If the object doesn't support the interface, it returns False. All that exception handler could catch are things like access violation and illegal operations, which you really shouldn't be catching there anyway.

You can implement that function more directly like this:

if Assigned(FDataSet) then
  Result := TCwcCDSAdapterNavBase.Create(...);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文