我有一个复杂的应用程序,在正常编译时按预期工作,但在使用FastMM 4.97(最新版本)编译时关闭表单会产生访问冲突。当消息调度尝试处理用于已经销毁的表单上的按钮的消息时,AV就会发生。我还为项目启用了Eurekalog 6.1.0.1(最新版本)。在禁用FastMM的FullDebug模式的情况下执行相同的代码时,它不会捕获任何运行时异常。
在某些情况下,FastMM是否可能会改变EXE的行为,从而导致或错误地报告访问冲突?
以下是FastMM错误报告:
--------------------------------2011/3/21 13:30:17--------------------------------
FastMM has detected an attempt to call a virtual method on a freed object. An access violation will now be raised in order to abort the current operation.
Freed object class: TftGenericButton80
Virtual method: Offset +80
Virtual method address: 4A1FF0
The allocation number was: 5628628
The object was allocated by thread 0xE80, and the stack trace (return addresses) at the time was:
403110 [System][@GetMem]
404F03 [System][TObject.NewInstance]
42E85D [FastMM4][CreateComponent]
42EAD9 [FastMM4][TReader.ReadComponent]
42FEE1 [Classes][TReader.ReadValue]
42ED86 [FastMM4][TReader.ReadDataInner]
42ECC5 [FastMM4][TReader.ReadData]
433802 [Classes][TComponent.ReadState]
4A21CE [Controls][TControl.ReadState]
4A5742 [Controls][TWinControl.ReadState]
48BCA0 [Forms][TCustomForm.ReadState]
The object was subsequently freed by thread 0xE80, and the stack trace (return addresses) at the time was:
40313B [System][@FreeMem]
404F21 [System][TObject.FreeInstance]
405339 [System][@ClassDestroy]
8AFBEF [..\..\AdvShapeButton.pas][AdvShapeButton][TAdvCustomShapeButton.Destroy][1422]
4A5601 [Controls][TWinControl.Destroy]
48A9DD [Forms][TScrollingWinControl.Destroy]
48B9D8 [Forms][TCustomForm.Destroy]
48B9F2 [Forms][TCustomForm.Destroy]
404F67 [System][TObject.Free]
A9C42C [..\..\fmWaitingList.pas][fmWaitingList][TfrmWaitingList.OnTanWaitingListItem][130]
A9D41B [fmWaitingListItem.pas][fmWaitingListItem][TfrmWaitingListItem.DoOnTanItem][142]
The current thread ID is 0xE80, and the stack trace (return addresses) leading to this error is:
8B2630 [..\..\AdvShapeButton.pas][AdvShapeButton][TAdvCustomShapeButton.Click][3042]
4A4817 [Controls][TControl.WMLButtonUp]
4A4227 [Controls][TControl.WndProc]
7E42B401 [CallNextHookEx]
7E42B401 [CallNextHookEx]
7E42B372 [MoveWindow]
7E42B317 [MoveWindow]
7E42B326 [MoveWindow]
7E42B326 [MoveWindow]
7E4278D0 [GetWindowTextLengthW]
7E4278E0 [GetWindowTextLengthW]
Current memory dump of 256 bytes starting at pointer address 7FEB5F00:
90 8C AD 00 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80
80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80
80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80
80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80
80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80
80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80
80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80
80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80
Œ . € € € € € € € € € € € € € € € € € € € € € € € € € € € €
€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € €
€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € €
€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € €
€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € €
€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € €
€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € €
€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € €
答案 0 :(得分:13)
它没有报道AV;它是创建一个虚假的AV异常,以便让你的进程停止,因为它所在的道路 - 访问已删除的对象 - 经常导致数据损坏,这更糟糕。
问题不是访问违规;问题是试图在已经释放的按钮上调用Click
方法。只是从查看此堆栈跟踪看起来,您或其他人已经安装了一个挂钩,该挂钩持有对按钮的引用,并且在窗体被销毁后不会自行更新。那是我开始寻找的地方。
答案 1 :(得分:7)
有点难以看清你的代码。但是从堆栈跟踪中,它看起来像是在处理发往该对象的消息的过程中可能正在销毁TAdvCustomShapeButton
实例。
开始检查的好地方应该是当前挂钩到TfrmWaitingList.OnTanWaitingListItem
事件的方法。
你说关闭表格时会发生这种情况 我假设您的表单在关闭时被销毁(不是简单地隐藏),因此它会破坏表单所拥有的所有对象。
表格是如何关闭的?你可能想要考虑Form.Release。
答案 2 :(得分:2)
我发现FastMM在这方面非常可靠。
如果在发布模式下运行,99.9%的时间在释放对象上调用方法都没有问题。 0.1%的时间只会出现在您最有价值的客户机器上!
所以,我敢打赌,这是一个问题。实际上很容易跟踪,因为FastMM为您提供了所有调用堆栈,它们确切地显示了它是如何发生的。您只需要仔细了解详细信息。
现在,您可能会问,如何在已释放的对象上调用方法?好吧,当你释放一个对象时,你将内存返回给内存管理器。但是内存管理器通常会保留该内存并等待重新使用它的适当时机。至关重要的是,它不会立即将其返回系统,因为这样做很昂贵(需要花费大量时间)。
这是使内存管理器快速运行的原因,但它也会导致错误,例如调用被屏蔽的自由对象上的方法。如果内存已返回系统,则此类操作将导致实际的访问冲突。这就是我所说的大部分时间,在内存管理器处于发布模式的情况下,这样的bug不会出现。
在我看来,这是FastMM的最佳功能之一,注意这个警告将为您节省未来的痛苦。试图在现场追踪这样的问题是非常困难的。使用FastMM提供的信息进行修复通常很简单。