Delphi 2009 - 接口属性会导致内存泄漏吗?
我继承了一个 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
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.
到目前为止,您已经得到了一些关于 FastMM 工作原理的很好的答案。 但至于您的实际问题,是的,接口对象可能会以两种不同的方式泄漏。
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.
如果您泄漏该类的 115 个实例,那么该类正在被泄漏。 正在泄漏的是该类占用的内存,而不是它所引用的事物占用的内存。 在某个地方,您有 115 个未释放的
TCwcBasicAdapter
实例。此外,属性不存储数据,无论它们是接口还是其他类型。 只有字段占用内存(以及编译器代表类分配的一些隐藏空间)。
所以,是的,你找错了树。 你的内存泄漏在其他地方。 当 FastMM 告诉您发生内存泄漏时,它不是也会告诉您每个泄漏实例的分配位置吗? 它有这个能力; 您可能需要调整一些条件编译符号才能启用该功能。
当然,泄漏的不仅仅是该类的实例。 FastMM 还应该报告一些其他泄漏的事情,例如实现该接口的类或类的实例。
根据您添加的函数,我开始怀疑确实是 TCwcCDSAdapterNavBase 发生了泄漏,这可能是因为您创建它时使用的非典型方式所致。
GetAdapterNav
中的异常处理程序是否运行过? 我对此表示怀疑;TObject.GetInterface
从不显式引发异常。 如果对象不支持该接口,则返回False
。 所有异常处理程序可以捕获的都是诸如访问冲突和非法操作之类的内容,无论如何您都不应该在那里捕获它们。您可以像这样更直接地实现该功能:
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 inGetAdapterNav
ever run? I doubt it;TObject.GetInterface
never explicitly raises an exception. If the object doesn't support the interface, it returnsFalse
. 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: