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

时间:2023-02-09 10:07:18

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.

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

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)

3 个解决方案

#1


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?

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

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.

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

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...

编辑:没有看到你的Object的代码实现ICwcCDSAdapterNav,我们无法判断它是否确实是引用计数。如果你不是从TInterfacedObject下降,很可能它不是引用计数,你不能依赖这个自动释放...

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.

您可能想看一下CodeRage 2会话:为傻瓜打击内存泄漏。它主要说明如何使用FastMM来防止/检测Delphi中的内存泄漏。适用于D2007,但仍适用于其他版本。

#2


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.

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

  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. 如果接口所属的对象在其_AddRef和_Release方法中实现了引用计数,则它们仅被引用计数。有些对象没有。

  3. 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.
  4. 如果您有循环接口引用(接口1引用接口2,它引用接口1),那么如果您没有一些特殊的技巧,引用计数将永远不会降为0。如果这是你的问题,我会向你推荐Andreas Hausladen最近关于这个主题的博客文章。

#3


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.

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

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.

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

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.

当然,不仅仅是那个班级的实例正在泄漏。 FastMM还应报告其他一些泄漏事件,例如实现该接口的类或类的实例。


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.

基于你添加的功能,我开始怀疑它真的是TCwcCDSAdapterNavBase正在泄漏,这可能是因为你用它来创建它的非典型方式。 GetAdapterNav中的异常处理程序是否运行?我对此表示怀疑; TObject.GetInterface从不显式引发异常。如果对象不支持该接口,则返回False。所有异常处理程序都可以捕获的是访问违规和非法操作,无论如何你真的不应该抓住它。

You can implement that function more directly like this:

您可以更直接地实现该功能,如下所示:

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

#1


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?

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

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.

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

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...

编辑:没有看到你的Object的代码实现ICwcCDSAdapterNav,我们无法判断它是否确实是引用计数。如果你不是从TInterfacedObject下降,很可能它不是引用计数,你不能依赖这个自动释放...

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.

您可能想看一下CodeRage 2会话:为傻瓜打击内存泄漏。它主要说明如何使用FastMM来防止/检测Delphi中的内存泄漏。适用于D2007,但仍适用于其他版本。

#2


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.

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

  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. 如果接口所属的对象在其_AddRef和_Release方法中实现了引用计数,则它们仅被引用计数。有些对象没有。

  3. 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.
  4. 如果您有循环接口引用(接口1引用接口2,它引用接口1),那么如果您没有一些特殊的技巧,引用计数将永远不会降为0。如果这是你的问题,我会向你推荐Andreas Hausladen最近关于这个主题的博客文章。

#3


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.

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

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.

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

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.

当然,不仅仅是那个班级的实例正在泄漏。 FastMM还应报告其他一些泄漏事件,例如实现该接口的类或类的实例。


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.

基于你添加的功能,我开始怀疑它真的是TCwcCDSAdapterNavBase正在泄漏,这可能是因为你用它来创建它的非典型方式。 GetAdapterNav中的异常处理程序是否运行?我对此表示怀疑; TObject.GetInterface从不显式引发异常。如果对象不支持该接口,则返回False。所有异常处理程序都可以捕获的是访问违规和非法操作,无论如何你真的不应该抓住它。

You can implement that function more directly like this:

您可以更直接地实现该功能,如下所示:

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