为什么会出现内存泄漏以及如何修复它?
unit Unit7;
interface
uses Classes;
type
TListener = class(TThread)
procedure Execute; override;
end;
TMyClass = class
o1,o2: Tobject;
procedure FreeMyObject(var obj: TObject);
constructor Create;
destructor Destroy; override;
end;
implementation
uses Windows, SysUtils;
var l: TListener;
my: TMyClass;
procedure TListener.Execute;
var msg:TMsg;
begin
while(GetMessage(msg, Cardinal(-1), 0, 0)) do
if(msg.message=6) then begin
TMyClass(msg.wParam).FreeMyObject(TObject(msg.lParam));
Exit;
end;
end;
constructor TMyClass.Create;
begin
inherited;
o1:=TObject.Create;
o2:=Tobject.Create; // Invalid pointer operation => mem leak
end;
destructor TMyClass.Destroy;
begin
if(Assigned(o1)) then o1.Free;
if(Assigned(o2)) then o2.Free;
inherited;
end;
procedure TMyClass.FreeMyObject(var obj: TObject);
begin
FreeAndNil(obj);
end;
initialization
l:= TListener.Create();
my:=TMyClass.Create;
sleep(1000); //make sure the message loop is set
PostThreadMessage(l.ThreadID, 6, Integer(my), Integer(my.o2));
finalization
l.Free;
my.Free;
end.
我使用消息处理程序来说明我的问题,以便您理解。真正的设计要复杂得多。函数“FreeMyObject”实际上使用多态性范例释放并创建一个实例,但这里不需要这样做。我只想指出设计应该保持不变。
现在问题和问题 - 为什么会发生以及如何解决它?看来“ifAssigned(o2)”不适合它。
我的想法:发送一个指向 my.o2 的指针会释放并且 nil o2 ,我尝试这样做,但是我无法在消息处理程序中从指针转换为对象,不知道为什么。
有人可以帮忙吗?谢谢
unit Unit7;
interface
uses Classes;
type
TListener = class(TThread)
procedure Execute; override;
end;
TMyClass = class
o1,o2: Tobject;
procedure FreeMyObject(var obj: TObject);
constructor Create;
destructor Destroy; override;
end;
implementation
uses Windows, SysUtils;
var l: TListener;
my: TMyClass;
procedure TListener.Execute;
var msg:TMsg;
begin
while(GetMessage(msg, Cardinal(-1), 0, 0)) do
if(msg.message=6) then begin
TMyClass(msg.wParam).FreeMyObject(TObject(msg.lParam));
Exit;
end;
end;
constructor TMyClass.Create;
begin
inherited;
o1:=TObject.Create;
o2:=Tobject.Create; // Invalid pointer operation => mem leak
end;
destructor TMyClass.Destroy;
begin
if(Assigned(o1)) then o1.Free;
if(Assigned(o2)) then o2.Free;
inherited;
end;
procedure TMyClass.FreeMyObject(var obj: TObject);
begin
FreeAndNil(obj);
end;
initialization
l:= TListener.Create();
my:=TMyClass.Create;
sleep(1000); //make sure the message loop is set
PostThreadMessage(l.ThreadID, 6, Integer(my), Integer(my.o2));
finalization
l.Free;
my.Free;
end.
I used the message handler to illustrate my problem as is so you understand it. The real design is a lot more complicated. The function 'FreeMyObject' actually Frees AND creates an instance using polymorphism paradigm, but this here is not needed. I only want to point out that the design should stay the same.
Now the question and problem - why it happens AND how to fix it? It seems 'if Assigned(o2)' doesn't fit it.
What I think of: Sending a pointer to my.o2 would free and nil o2 and I tries to do so, but I couldn't convert from pointer to object in the message handler, got no idea why.
Could anybody give a hand? Thanks
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您释放了
o2
两次。一次是消息的结果,一次是析构函数的结果。您认为在调用
FreeMyObject
时将o2
设置为nil
,但事实并非如此。实际上,您将msg.lParam
设置为 0。o2
是一个保存对象引用的变量。您正在传递o2
的值,并且当您按值传递时,您无法修改所传递值的变量。因此您需要传递对o2
的引用。为此,您需要添加额外级别的重定向并将指针传递给o2
,如下所示:您不需要
FreeMyObject
,只需调用直接使用 FreeAndNil
。并且您不需要在消息中传递实例。我希望你真正的代码不会像这样奇怪! ;-)
You free
o2
twice. Once as a result of the message and once from the destructor.You think you are setting
o2
tonil
when you callFreeMyObject
but you are not. You are in fact settingmsg.lParam
to 0.o2
is a variable holding a reference to an object. You are passing the value ofo2
and when you pass by value you cannot modify the variable whose value you passed. So you need to pass a reference too2
. To do so you need to add an extra level of redirection and pass a pointer too2
, like so:You don't need
FreeMyObject
, you can just callFreeAndNil
directly. And you don't need to pass an instance in the message.I hope your real code isn't quite as weird as this! ;-)
如果您想要
FreeAndNil
对象仅发送对象引用Integer(my.o2)
是不够的 - 您需要Integer(@my.o2)
>。您还应该对代码进行相应的更改。由于您的代码很难调试,我编写了一个简单的演示来给出必要的代码更改的想法:
If you want to
FreeAndNil
an object sending just object referenceInteger(my.o2)
is not enough - you needInteger(@my.o2)
. You should also make corresponding changes in your code.Since your code is difficult to debug I have written a simple demo to give an idea of necessary code changes:
发生的事情是这样的:
程序启动。初始化运行并向线程发送一条消息,该线程对传入的引用调用
FreeAndNil
。这会将传入的引用设置为 nil,但它不会将保存o2
的对象字段设置为 nil。那是不同的参考。然后在析构函数中,由于该字段不是nil,因此它会尝试再次释放它,并且您会收到双重释放错误(无效指针操作异常)。由于您在析构函数中引发了异常,因此 TMyClass 永远不会被销毁,并且您会从中得到内存泄漏。
如果您想正确执行此操作,请将某种类型的标识符传递给 FreeMyObject 而不是引用。例如整数
2
或字符串o2
。然后让FreeMyObject
使用此值来查找应该调用FreeAndNil
的内容。 (如果您有 Delphi 2010 或更高版本,则使用 RTTI 可以很容易地做到这一点。)这需要更多的工作,但它会修复您看到的错误。Here's what's going on:
Program starts. Initialization runs and sends a message to the thread, which calls
FreeAndNil
on the reference that gets passed in. This sets the reference that gets passed in to nil, but it does not set the object field holdingo2
to nil. That's a different reference.Then in the destructor, since the field isn't nil, it tries to free it again and you get a double-free error (invalid pointer operation exception). Since you raised an exception in the destructor, the TMyClass never gets destroyed and you get a memory leak from it.
If you want to do this right, pass an identifier of some type to
FreeMyObject
instead of a reference. Like an integer2
, or a stringo2
. Then haveFreeMyObject
use this value to look up what it should be callingFreeAndNil
on. (If you have Delphi 2010 or later, that's pretty easy to do with RTTI.) It's a little more work, but it will fix the errors you're seeing.